diff --git a/DEPS b/DEPS index af17a5d9..80b86f85 100644 --- a/DEPS +++ b/DEPS
@@ -235,7 +235,7 @@ # luci-go CIPD package version. # Make sure the revision is uploaded by infra-packagers builder. # https://ci.chromium.org/p/infra-internal/g/infra-packagers/console - 'luci_go': 'git_revision:4494da54d1ba43d9127c07630d452c11ba2c953e', + 'luci_go': 'git_revision:3569ebf36f17a991aa4d26fd6e228cdf6e664d13', # This can be overridden, e.g. with custom_vars, to build clang from HEAD # instead of downloading the prebuilt pinned revision. @@ -304,15 +304,15 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling Skia # and whatever else without interference from each other. - 'skia_revision': '528ff00a3637f6fef419b69392c803f8f2a1e778', + 'skia_revision': 'f7064a1861e13b87cdfaafb11410c7e68c1f440d', # 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': '5f7f064aee9828f22e29abdc45b63e6c9b854298', + 'v8_revision': '27bc33ed3812654d8efaa1e640955adf6afd6632', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling ANGLE # and whatever else without interference from each other. - 'angle_revision': 'fbb16d64636b352e2a6c68b8dfc8bbd5e5918585', + 'angle_revision': '1ee27fcdbfe6ad6138907783cd43b83d6720f390', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling SwiftShader # and whatever else without interference from each other. @@ -391,7 +391,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': 'a09c4de27878ff6f526c3b78f53bf99e6495f1d3', + 'devtools_frontend_revision': '9ba6ed7f9e83373c5395aa30c66dd6c81f0efec0', # 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. @@ -431,7 +431,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. - 'dawn_revision': '86f01195ceaff80fc20498a7fb1a0ad70a6c366e', + 'dawn_revision': 'df714941d3a24b7d7a6e7e2a5475c8e8a76a81f8', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. @@ -794,7 +794,7 @@ 'src/clank': { 'url': Var('chrome_git') + '/clank/internal/apps.git' + '@' + - '3935d163b8c4d7775a19b87a678849992e18f255', + 'cf8ffc879eb73497b886ddfe4e901c274b96c435', 'condition': 'checkout_android and checkout_src_internal', }, @@ -983,7 +983,7 @@ 'packages': [ { 'package': 'chromium/third_party/androidx', - 'version': 'S5-ILgSdnMEpl88ZRDuQOJ0Ds_HZR3JAKxZzSoj5toYC', + 'version': 'zd34H0ZKUoP4415kneK7fwSS-MzZu0_hYScGJWzsVgkC', }, ], 'condition': 'checkout_android', @@ -1234,13 +1234,13 @@ }, 'src/third_party/depot_tools': - Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '33a8f072364d819b69cfb45958e17aca021150ad', + Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'df528a9d7a40d52f0cf0b61cf21e1e298f67860a', 'src/third_party/devtools-frontend/src': Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'), 'src/third_party/devtools-frontend-internal': { - 'url': Var('chrome_git') + '/devtools/devtools-internal.git' + '@' + '1edfd44e2837c5219e24e004e31602c19ca6a9c2', + 'url': Var('chrome_git') + '/devtools/devtools-internal.git' + '@' + '261f701a7f57b23ea170314c1925d3743858b496', 'condition': 'checkout_src_internal', }, @@ -1891,7 +1891,7 @@ Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '7e014d4d99cdacd0c08a711d379fcb7886ed66d2', 'src/third_party/webrtc': - Var('webrtc_git') + '/src.git' + '@' + '90d5e7d65520656ddec367cd454bffc16d55c423', + Var('webrtc_git') + '/src.git' + '@' + 'ef5cd7d336ffa0b388a5b2140ef7a6ec31b69b75', # Wuffs' canonical repository is at github.com/google/wuffs, but we use # Skia's mirror of Wuffs, the same as in upstream Skia's DEPS file. @@ -1968,7 +1968,7 @@ Var('chromium_git') + '/v8/v8.git' + '@' + Var('v8_revision'), 'src-internal': { - 'url': Var('chrome_git') + '/chrome/src-internal.git@24d159fca4f5a3e4a816bfab61767356a578dfa5', + 'url': Var('chrome_git') + '/chrome/src-internal.git@8f94ab55bf24a2399e85050aaaa74f172632d0cf', 'condition': 'checkout_src_internal', }, @@ -2009,7 +2009,7 @@ 'packages': [ { 'package': 'chromeos_internal/apps/media_app/app', - 'version': 'eidV3Okk2cLuns-cpL0H5ywKTXHCLHczvGCJXBYyPv4C', + 'version': 'AvGNSo5SPRLnsrDFsxN4Yxo5F9rx1WPz0-DYmEEDkGMC', }, ], 'condition': 'checkout_chromeos and checkout_src_internal',
diff --git a/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java b/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java index 695fd5b..ddfec4f 100644 --- a/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java +++ b/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
@@ -23,6 +23,7 @@ import org.chromium.content_public.common.ContentSwitches; import org.chromium.gpu.config.GpuFeatures; import org.chromium.gpu.config.GpuSwitches; +import org.chromium.net.NetFeatures; import org.chromium.services.network.NetworkServiceFeatures; import org.chromium.ui.accessibility.AccessibilityFeatures; @@ -389,6 +390,9 @@ Flag.baseFeature("LessChattyNetworkService"), Flag.baseFeature(BlinkFeatures.AUTOFILL_DETECT_REMOVED_FORM_CONTROLS, "Enables Autofill to detect if form controls are removed from the DOM"), + Flag.baseFeature(NetFeatures.SUPPORT_PARTITIONED_BLOB_URL, + "Enables the new Blob URL implementation needed for third-party storage" + + " partitioning"), // Add new commandline switches and features above. The final entry should have a // trailing comma for cleaner diffs. };
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AcceptLanguageTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AcceptLanguageTest.java index 0ad75e76..6970362 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/AcceptLanguageTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/AcceptLanguageTest.java
@@ -5,8 +5,8 @@ package org.chromium.android_webview.test; import android.os.LocaleList; -import android.support.test.InstrumentationRegistry; +import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import org.junit.After;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AndroidScrollIntegrationTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AndroidScrollIntegrationTest.java index e65180d..0452090fe 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/AndroidScrollIntegrationTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/AndroidScrollIntegrationTest.java
@@ -9,8 +9,8 @@ import android.annotation.SuppressLint; import android.content.Context; import android.graphics.Point; -import android.support.test.InstrumentationRegistry; +import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import org.junit.After;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AndroidViewIntegrationTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AndroidViewIntegrationTest.java index 593355c..ec925764 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/AndroidViewIntegrationTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/AndroidViewIntegrationTest.java
@@ -4,11 +4,11 @@ package org.chromium.android_webview.test; -import android.support.test.InstrumentationRegistry; import android.view.View; import android.view.ViewGroup.LayoutParams; import android.widget.LinearLayout; +import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import org.junit.Assert;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwActivityTestRule.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwActivityTestRule.java index b29e8a2..6c90d32 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/AwActivityTestRule.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwActivityTestRule.java
@@ -6,12 +6,13 @@ import android.content.Context; import android.content.Intent; -import android.support.test.InstrumentationRegistry; -import android.support.test.runner.lifecycle.Stage; import android.util.AndroidRuntimeException; import android.util.Base64; import android.view.ViewGroup; +import androidx.test.InstrumentationRegistry; +import androidx.test.runner.lifecycle.Stage; + import org.junit.Assert; import org.junit.runner.Description; import org.junit.runners.model.Statement;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwAutofillTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwAutofillTest.java index 04b702d..104d7b3e 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/AwAutofillTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwAutofillTest.java
@@ -19,7 +19,6 @@ import android.os.Bundle; import android.os.IBinder; import android.os.SystemClock; -import android.support.test.InstrumentationRegistry; import android.text.TextUtils; import android.util.Pair; import android.util.SparseArray; @@ -29,6 +28,7 @@ import android.view.autofill.AutofillValue; import androidx.annotation.RequiresApi; +import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import org.junit.After;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientCallbackHelperTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientCallbackHelperTest.java index 4cbf2ea..40e1872 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientCallbackHelperTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientCallbackHelperTest.java
@@ -9,8 +9,8 @@ import android.graphics.Picture; import android.os.Handler; import android.os.Looper; -import android.support.test.InstrumentationRegistry; +import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import org.junit.Assert;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientFullScreenTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientFullScreenTest.java index 06bfff1c..49e5d01 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientFullScreenTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientFullScreenTest.java
@@ -6,11 +6,11 @@ import static org.chromium.android_webview.test.AwActivityTestRule.WAIT_TIMEOUT_MS; -import android.support.test.InstrumentationRegistry; import android.view.KeyEvent; import android.view.View; import android.view.ViewGroup; +import androidx.test.InstrumentationRegistry; import androidx.test.filters.MediumTest; import org.hamcrest.Matchers;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientGetDefaultVideoPosterTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientGetDefaultVideoPosterTest.java index cd1f54bc..f33553a 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientGetDefaultVideoPosterTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientGetDefaultVideoPosterTest.java
@@ -7,9 +7,9 @@ import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; -import android.support.test.InstrumentationRegistry; import android.util.Log; +import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import org.junit.Assert;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientOnFormResubmissionTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientOnFormResubmissionTest.java index 363ddce..92d6dc0 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientOnFormResubmissionTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientOnFormResubmissionTest.java
@@ -5,9 +5,9 @@ package org.chromium.android_webview.test; import android.os.Message; -import android.support.test.InstrumentationRegistry; import android.util.Base64; +import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import org.junit.After;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientShouldInterceptRequestTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientShouldInterceptRequestTest.java index 9a06916..b205fe9b 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientShouldInterceptRequestTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientShouldInterceptRequestTest.java
@@ -6,10 +6,10 @@ import static org.chromium.android_webview.test.AwActivityTestRule.SCALED_WAIT_TIMEOUT_MS; -import android.support.test.InstrumentationRegistry; import android.util.Pair; import android.webkit.JavascriptInterface; +import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import com.google.common.util.concurrent.SettableFuture;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientShouldOverrideUrlLoadingTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientShouldOverrideUrlLoadingTest.java index 85d1f72..21af506e 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientShouldOverrideUrlLoadingTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientShouldOverrideUrlLoadingTest.java
@@ -9,9 +9,9 @@ import android.annotation.SuppressLint; import android.os.Build; -import android.support.test.InstrumentationRegistry; import android.util.Pair; +import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import org.hamcrest.Matchers;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsGarbageCollectionTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsGarbageCollectionTest.java index d8a9d9be..b5e7ab1 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsGarbageCollectionTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsGarbageCollectionTest.java
@@ -11,12 +11,12 @@ import android.content.ContextWrapper; import android.os.Build; import android.os.ResultReceiver; -import android.support.test.InstrumentationRegistry; import android.util.Pair; import android.view.Window; import android.view.WindowManager; import android.webkit.JavascriptInterface; +import androidx.test.InstrumentationRegistry; import androidx.test.filters.LargeTest; import androidx.test.filters.SmallTest;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsRenderTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsRenderTest.java index 71a6227b..45aaca1 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsRenderTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsRenderTest.java
@@ -6,9 +6,9 @@ import android.graphics.Bitmap; import android.graphics.Color; -import android.support.test.InstrumentationRegistry; import android.view.View; +import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import org.junit.Assert;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsStaticsTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsStaticsTest.java index 5e54e59..a6763dd 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsStaticsTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsStaticsTest.java
@@ -4,8 +4,7 @@ package org.chromium.android_webview.test; -import android.support.test.InstrumentationRegistry; - +import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import org.junit.Assert;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsTest.java index ae0c64c..8194a8f3 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsTest.java
@@ -19,12 +19,12 @@ import android.os.Handler; import android.os.Looper; import android.os.Message; -import android.support.test.InstrumentationRegistry; import android.util.Pair; import android.view.KeyEvent; import android.view.View; import android.webkit.JavascriptInterface; +import androidx.test.InstrumentationRegistry; import androidx.test.filters.LargeTest; import androidx.test.filters.MediumTest; import androidx.test.filters.SmallTest;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwFormDatabaseTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwFormDatabaseTest.java index d99b9e36d..43d0b2b 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/AwFormDatabaseTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwFormDatabaseTest.java
@@ -4,8 +4,7 @@ package org.chromium.android_webview.test; -import android.support.test.InstrumentationRegistry; - +import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import org.junit.Assert;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwJavaBridgeTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwJavaBridgeTest.java index 1ccf94ba..5657d47b 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/AwJavaBridgeTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwJavaBridgeTest.java
@@ -4,9 +4,9 @@ package org.chromium.android_webview.test; -import android.support.test.InstrumentationRegistry; import android.webkit.JavascriptInterface; +import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import org.junit.Assert;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwLegacyQuirksTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwLegacyQuirksTest.java index b654ceca..8cc1ccd 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/AwLegacyQuirksTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwLegacyQuirksTest.java
@@ -4,8 +4,7 @@ package org.chromium.android_webview.test; -import android.support.test.InstrumentationRegistry; - +import androidx.test.InstrumentationRegistry; import androidx.test.filters.MediumTest; import org.junit.Assert;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwMetricsIntegrationTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwMetricsIntegrationTest.java index 58d3f65..7f474706 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/AwMetricsIntegrationTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwMetricsIntegrationTest.java
@@ -12,8 +12,7 @@ import static org.chromium.android_webview.test.OnlyRunIn.ProcessMode.MULTI_PROCESS; -import android.support.test.InstrumentationRegistry; - +import androidx.test.InstrumentationRegistry; import androidx.test.filters.MediumTest; import org.hamcrest.Description;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwNetworkConfigurationTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwNetworkConfigurationTest.java index 864529ad..6289c9ef 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/AwNetworkConfigurationTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwNetworkConfigurationTest.java
@@ -7,9 +7,9 @@ import static org.chromium.android_webview.test.AwActivityTestRule.WAIT_TIMEOUT_MS; import android.os.Build; -import android.support.test.InstrumentationRegistry; import android.webkit.JavascriptInterface; +import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import com.google.common.util.concurrent.SettableFuture;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwQuotaManagerBridgeTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwQuotaManagerBridgeTest.java index 8a495e0..ff8fb80 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/AwQuotaManagerBridgeTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwQuotaManagerBridgeTest.java
@@ -4,7 +4,7 @@ package org.chromium.android_webview.test; -import android.support.test.InstrumentationRegistry; +import androidx.test.InstrumentationRegistry; import org.junit.After; import org.junit.Assert;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwRestrictSensitiveContentTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwRestrictSensitiveContentTest.java index 93c8216..2381b36 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/AwRestrictSensitiveContentTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwRestrictSensitiveContentTest.java
@@ -5,9 +5,9 @@ package org.chromium.android_webview.test; import android.content.Context; -import android.support.test.InstrumentationRegistry; import android.util.Pair; +import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import org.junit.After;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwSecondBrowserProcessTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwSecondBrowserProcessTest.java index 899a167..d9f21c3 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/AwSecondBrowserProcessTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwSecondBrowserProcessTest.java
@@ -13,7 +13,8 @@ import android.os.Parcel; import android.os.Process; import android.os.RemoteException; -import android.support.test.InstrumentationRegistry; + +import androidx.test.InstrumentationRegistry; import org.junit.After; import org.junit.Assert;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwSettingsTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwSettingsTest.java index 3cd416c..5be64a7 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/AwSettingsTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwSettingsTest.java
@@ -11,11 +11,11 @@ import android.net.http.SslError; import android.os.Build; import android.os.SystemClock; -import android.support.test.InstrumentationRegistry; import android.view.WindowManager; import android.webkit.JavascriptInterface; import android.webkit.WebSettings; +import androidx.test.InstrumentationRegistry; import androidx.test.filters.LargeTest; import androidx.test.filters.MediumTest; import androidx.test.filters.SmallTest;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwStrictModeTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwStrictModeTest.java index 3ea20cf9..493f77f 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/AwStrictModeTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwStrictModeTest.java
@@ -5,8 +5,8 @@ package org.chromium.android_webview.test; import android.os.StrictMode; -import android.support.test.InstrumentationRegistry; +import androidx.test.InstrumentationRegistry; import androidx.test.filters.LargeTest; import org.junit.After;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwUncaughtExceptionTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwUncaughtExceptionTest.java index f8ebd22..ab5bfbe 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/AwUncaughtExceptionTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwUncaughtExceptionTest.java
@@ -7,10 +7,10 @@ import static org.chromium.android_webview.test.AwActivityTestRule.SCALED_WAIT_TIMEOUT_MS; import android.os.Looper; -import android.support.test.runner.lifecycle.ActivityLifecycleMonitor; -import android.support.test.runner.lifecycle.ActivityLifecycleMonitorRegistry; import androidx.test.filters.MediumTest; +import androidx.test.runner.lifecycle.ActivityLifecycleMonitor; +import androidx.test.runner.lifecycle.ActivityLifecycleMonitorRegistry; import org.junit.After; import org.junit.Assert;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwWebContentsObserverTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwWebContentsObserverTest.java index f8c36e2c..bf6abbe 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/AwWebContentsObserverTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwWebContentsObserverTest.java
@@ -4,8 +4,7 @@ package org.chromium.android_webview.test; -import android.support.test.InstrumentationRegistry; - +import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import org.junit.Assert;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/ClearHistoryTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/ClearHistoryTest.java index daf0d180..94cbe6f 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/ClearHistoryTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/ClearHistoryTest.java
@@ -4,8 +4,7 @@ package org.chromium.android_webview.test; -import android.support.test.InstrumentationRegistry; - +import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import org.junit.Assert;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/ClientAddMessageToConsoleTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/ClientAddMessageToConsoleTest.java index cf15c25..6956848 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/ClientAddMessageToConsoleTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/ClientAddMessageToConsoleTest.java
@@ -4,8 +4,7 @@ package org.chromium.android_webview.test; -import android.support.test.InstrumentationRegistry; - +import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import org.junit.Assert;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/ClientHintsTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/ClientHintsTest.java index 64fe0b0..4a5835e 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/ClientHintsTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/ClientHintsTest.java
@@ -6,8 +6,7 @@ import static org.chromium.android_webview.test.AwActivityTestRule.WAIT_TIMEOUT_MS; -import android.support.test.InstrumentationRegistry; - +import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import org.json.JSONObject;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/ClientOnPageFinishedTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/ClientOnPageFinishedTest.java index e25a02f..cf9714f 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/ClientOnPageFinishedTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/ClientOnPageFinishedTest.java
@@ -6,9 +6,9 @@ import static org.chromium.android_webview.test.AwActivityTestRule.SCALED_WAIT_TIMEOUT_MS; -import android.support.test.InstrumentationRegistry; import android.util.Pair; +import androidx.test.InstrumentationRegistry; import androidx.test.filters.MediumTest; import org.junit.Assert;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/ContentViewMiscTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/ContentViewMiscTest.java index 1f10f2e..f2ab7ec 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/ContentViewMiscTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/ContentViewMiscTest.java
@@ -10,8 +10,8 @@ import android.net.Proxy; import android.os.Handler; import android.os.Looper; -import android.support.test.InstrumentationRegistry; +import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import org.junit.Assert;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/CookieManagerStartupTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/CookieManagerStartupTest.java index f8b4428..20d1af9 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/CookieManagerStartupTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/CookieManagerStartupTest.java
@@ -5,8 +5,8 @@ package org.chromium.android_webview.test; import android.os.Looper; -import android.support.test.InstrumentationRegistry; +import androidx.test.InstrumentationRegistry; import androidx.test.filters.MediumTest; import androidx.test.filters.SmallTest;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/CookieManagerTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/CookieManagerTest.java index d46cc8e4..2e8bb2d 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/CookieManagerTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/CookieManagerTest.java
@@ -8,10 +8,10 @@ import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.MatcherAssert.assertThat; -import android.support.test.InstrumentationRegistry; import android.util.Pair; import androidx.annotation.IntDef; +import androidx.test.InstrumentationRegistry; import androidx.test.filters.MediumTest; import androidx.test.filters.SmallTest;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/FencedFrameTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/FencedFrameTest.java index efe08f2..116cfc4 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/FencedFrameTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/FencedFrameTest.java
@@ -7,11 +7,11 @@ import static org.chromium.android_webview.test.AwActivityTestRule.WAIT_TIMEOUT_MS; import android.graphics.Color; -import android.support.test.InstrumentationRegistry; import android.util.Pair; import android.webkit.JavascriptInterface; import android.webkit.WebView.HitTestResult; +import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import org.junit.After;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/GeolocationTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/GeolocationTest.java index 3fc06184..ac929a4 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/GeolocationTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/GeolocationTest.java
@@ -5,8 +5,8 @@ package org.chromium.android_webview.test; import android.content.Context; -import android.support.test.InstrumentationRegistry; +import androidx.test.InstrumentationRegistry; import androidx.test.filters.MediumTest; import androidx.test.filters.SmallTest;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/HttpAuthDatabaseTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/HttpAuthDatabaseTest.java index a357f94..82a99ae 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/HttpAuthDatabaseTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/HttpAuthDatabaseTest.java
@@ -6,8 +6,7 @@ import static org.chromium.android_webview.test.OnlyRunIn.ProcessMode.SINGLE_PROCESS; -import android.support.test.InstrumentationRegistry; - +import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import org.junit.After;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/LoadDataWithBaseUrlTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/LoadDataWithBaseUrlTest.java index 76baaf2..f2355fc 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/LoadDataWithBaseUrlTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/LoadDataWithBaseUrlTest.java
@@ -5,9 +5,9 @@ package org.chromium.android_webview.test; import android.graphics.Bitmap; -import android.support.test.InstrumentationRegistry; import android.util.Pair; +import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import org.junit.Assert;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/LoadUrlTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/LoadUrlTest.java index 7ee237c..8ee67c51 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/LoadUrlTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/LoadUrlTest.java
@@ -7,11 +7,11 @@ import static org.chromium.android_webview.test.AwActivityTestRule.WAIT_TIMEOUT_MS; import android.content.Context; -import android.support.test.InstrumentationRegistry; import android.util.Base64; import android.util.Pair; import android.view.ViewGroup; +import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import org.json.JSONArray;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/ManifestMetadataUtilTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/ManifestMetadataUtilTest.java index 5065017..9a83d34f 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/ManifestMetadataUtilTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/ManifestMetadataUtilTest.java
@@ -6,8 +6,8 @@ import android.content.ComponentName; import android.os.Bundle; -import android.support.test.InstrumentationRegistry; +import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import org.junit.Assert;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/NavigationHistoryTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/NavigationHistoryTest.java index 91b3a14..fd1d0b3 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/NavigationHistoryTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/NavigationHistoryTest.java
@@ -4,8 +4,7 @@ package org.chromium.android_webview.test; -import android.support.test.InstrumentationRegistry; - +import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import org.junit.After;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/OnDiskFileTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/OnDiskFileTest.java index 26d8e79..99c7a09 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/OnDiskFileTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/OnDiskFileTest.java
@@ -5,8 +5,8 @@ package org.chromium.android_webview.test; import android.content.Context; -import android.support.test.InstrumentationRegistry; +import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import org.junit.Assert;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/PolicyUrlFilteringTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/PolicyUrlFilteringTest.java index 9164c497..086735505 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/PolicyUrlFilteringTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/PolicyUrlFilteringTest.java
@@ -6,9 +6,9 @@ import static org.chromium.android_webview.test.OnlyRunIn.ProcessMode.SINGLE_PROCESS; -import android.support.test.InstrumentationRegistry; import android.util.Pair; +import androidx.test.InstrumentationRegistry; import androidx.test.filters.MediumTest; import androidx.test.filters.SmallTest;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/PopupWindowTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/PopupWindowTest.java index 62c20d9..87c1dc4 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/PopupWindowTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/PopupWindowTest.java
@@ -6,9 +6,9 @@ import android.graphics.Rect; import android.net.Uri; -import android.support.test.InstrumentationRegistry; import android.webkit.JavascriptInterface; +import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import org.hamcrest.Matchers;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/PostMessageTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/PostMessageTest.java index 67eca22..1eb3bb97 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/PostMessageTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/PostMessageTest.java
@@ -9,9 +9,9 @@ import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; -import android.support.test.InstrumentationRegistry; import android.webkit.JavascriptInterface; +import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import org.hamcrest.Matchers;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/SafeBrowsingTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/SafeBrowsingTest.java index 31c1772..f257ab4 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/SafeBrowsingTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/SafeBrowsingTest.java
@@ -13,9 +13,9 @@ import android.graphics.Color; import android.net.Uri; import android.os.Build; -import android.support.test.InstrumentationRegistry; import android.view.ViewGroup; +import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import org.hamcrest.Matchers;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/SaveRestoreStateTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/SaveRestoreStateTest.java index 6da760824..316af86c 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/SaveRestoreStateTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/SaveRestoreStateTest.java
@@ -5,8 +5,8 @@ package org.chromium.android_webview.test; import android.os.Bundle; -import android.support.test.InstrumentationRegistry; +import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import org.junit.After;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/SslPreferencesTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/SslPreferencesTest.java index 601ebeea..28b144fc 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/SslPreferencesTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/SslPreferencesTest.java
@@ -5,8 +5,8 @@ package org.chromium.android_webview.test; import android.net.http.SslError; -import android.support.test.InstrumentationRegistry; +import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import org.junit.Assert;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/VisualStateTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/VisualStateTest.java index 80b264d..bd44f78 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/VisualStateTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/VisualStateTest.java
@@ -10,9 +10,9 @@ import android.graphics.Bitmap; import android.graphics.Color; import android.graphics.Rect; -import android.support.test.InstrumentationRegistry; import android.util.Base64; +import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import org.junit.Assert;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/WebKitHitTestTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/WebKitHitTestTest.java index 1178499b..be4da908 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/WebKitHitTestTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/WebKitHitTestTest.java
@@ -9,10 +9,10 @@ import android.os.Handler; import android.os.Message; import android.os.SystemClock; -import android.support.test.InstrumentationRegistry; import android.view.KeyEvent; import android.webkit.WebView.HitTestResult; +import androidx.test.InstrumentationRegistry; import androidx.test.filters.LargeTest; import androidx.test.filters.SmallTest;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/WebViewAsynchronousFindApisTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/WebViewAsynchronousFindApisTest.java index 2ac0449..d1c1fe34 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/WebViewAsynchronousFindApisTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/WebViewAsynchronousFindApisTest.java
@@ -4,8 +4,7 @@ package org.chromium.android_webview.test; -import android.support.test.InstrumentationRegistry; - +import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import org.junit.Assert;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/WebViewFindApisTestRule.java b/android_webview/javatests/src/org/chromium/android_webview/test/WebViewFindApisTestRule.java index 8329fd1d..6154915 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/WebViewFindApisTestRule.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/WebViewFindApisTestRule.java
@@ -4,7 +4,7 @@ package org.chromium.android_webview.test; -import android.support.test.InstrumentationRegistry; +import androidx.test.InstrumentationRegistry; import org.junit.runner.Description; import org.junit.runners.model.Statement;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/devui/ComponentsListFragmentTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/devui/ComponentsListFragmentTest.java index 2e99028..100d36b 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/devui/ComponentsListFragmentTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/devui/ComponentsListFragmentTest.java
@@ -17,8 +17,8 @@ import android.content.Context; import android.content.Intent; -import android.support.test.InstrumentationRegistry; +import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import org.junit.After;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/devui/ComponentsListFragmentUpdateButtonTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/devui/ComponentsListFragmentUpdateButtonTest.java index 44ec9ce..58e31f5 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/devui/ComponentsListFragmentUpdateButtonTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/devui/ComponentsListFragmentUpdateButtonTest.java
@@ -16,8 +16,8 @@ import android.content.Context; import android.content.Intent; -import android.support.test.InstrumentationRegistry; +import androidx.test.InstrumentationRegistry; import androidx.test.filters.MediumTest; import org.junit.After;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/devui/CrashesListFragmentTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/devui/CrashesListFragmentTest.java index 711851246..fec8f2c17 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/devui/CrashesListFragmentTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/devui/CrashesListFragmentTest.java
@@ -41,11 +41,11 @@ import android.graphics.Canvas; import android.graphics.drawable.Drawable; import android.os.Build; -import android.support.test.InstrumentationRegistry; import android.view.View; import android.widget.ImageView; import androidx.annotation.IdRes; +import androidx.test.InstrumentationRegistry; import androidx.test.espresso.DataInteraction; import androidx.test.espresso.intent.Intents; import androidx.test.espresso.intent.matcher.IntentMatchers;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/devui/DeveloperUiTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/devui/DeveloperUiTest.java index 0abc802..cd73ba8 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/devui/DeveloperUiTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/devui/DeveloperUiTest.java
@@ -35,9 +35,9 @@ import android.content.Intent; import android.content.SharedPreferences; import android.provider.Settings; -import android.support.test.InstrumentationRegistry; import android.view.View; +import androidx.test.InstrumentationRegistry; import androidx.test.espresso.DataInteraction; import androidx.test.espresso.intent.Intents; import androidx.test.espresso.intent.matcher.IntentMatchers;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/devui/FlagsFragmentTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/devui/FlagsFragmentTest.java index d411bba..5b95c0f 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/devui/FlagsFragmentTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/devui/FlagsFragmentTest.java
@@ -34,7 +34,6 @@ import android.graphics.Color; import android.graphics.drawable.Drawable; import android.os.SystemClock; -import android.support.test.InstrumentationRegistry; import android.text.SpannableString; import android.text.style.BackgroundColorSpan; import android.view.MotionEvent; @@ -44,6 +43,7 @@ import android.widget.TextView; import androidx.annotation.IntDef; +import androidx.test.InstrumentationRegistry; import androidx.test.espresso.DataInteraction; import androidx.test.espresso.Espresso; import androidx.test.espresso.NoMatchingRootException;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/devui/HomeFragmentTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/devui/HomeFragmentTest.java index 20f740c..c7752178 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/devui/HomeFragmentTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/devui/HomeFragmentTest.java
@@ -31,8 +31,8 @@ import android.content.pm.PackageInfo; import android.os.Build; import android.provider.Settings; -import android.support.test.InstrumentationRegistry; +import androidx.test.InstrumentationRegistry; import androidx.test.espresso.intent.Intents; import androidx.test.espresso.intent.matcher.IntentMatchers; import androidx.test.filters.MediumTest;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/util/AwQuotaManagerBridgeTestUtil.java b/android_webview/javatests/src/org/chromium/android_webview/test/util/AwQuotaManagerBridgeTestUtil.java index 46430552..e2b367b 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/util/AwQuotaManagerBridgeTestUtil.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/util/AwQuotaManagerBridgeTestUtil.java
@@ -4,7 +4,7 @@ package org.chromium.android_webview.test.util; -import android.support.test.InstrumentationRegistry; +import androidx.test.InstrumentationRegistry; import org.chromium.android_webview.AwQuotaManagerBridge; import org.chromium.base.test.util.CallbackHelper;
diff --git a/android_webview/test/BUILD.gn b/android_webview/test/BUILD.gn index b6c6874e..18cd1b6 100644 --- a/android_webview/test/BUILD.gn +++ b/android_webview/test/BUILD.gn
@@ -118,8 +118,9 @@ "//components/heap_profiling/multi_process:heap_profiling_java_test_support", "//components/policy/android:policy_java_test_support", "//content/public/android:content_java", - "//third_party/android_support_test_runner:runner_java", "//third_party/androidx:androidx_annotation_annotation_java", + "//third_party/androidx:androidx_test_monitor_java", + "//third_party/androidx:androidx_test_runner_java", "//third_party/androidx_javascriptengine:javascriptengine_java", "//third_party/junit", "//ui/android:ui_java", @@ -292,13 +293,12 @@ "//third_party/android_deps:guava_android_java", "//third_party/android_deps:guava_android_java", "//third_party/android_deps:protobuf_lite_runtime_java", - "//third_party/android_support_test_runner:rules_java", - "//third_party/android_support_test_runner:runner_java", "//third_party/androidx:androidx_activity_activity_java", "//third_party/androidx:androidx_annotation_annotation_java", "//third_party/androidx:androidx_core_core_java", "//third_party/androidx:androidx_fragment_fragment_java", "//third_party/androidx:androidx_test_core_java", + "//third_party/androidx:androidx_test_monitor_java", "//third_party/androidx:androidx_test_rules_java", "//third_party/androidx:androidx_test_runner_java", "//third_party/androidx_javascriptengine:javascriptengine_java", @@ -712,8 +712,8 @@ "//content/public/android:content_full_java", "//content/public/test/android:content_java_test_support", "//third_party/android_deps:protobuf_lite_runtime_java", - "//third_party/android_support_test_runner:runner_java", "//third_party/androidx:androidx_test_core_java", + "//third_party/androidx:androidx_test_monitor_java", "//third_party/androidx:androidx_test_runner_java", "//third_party/blink/public/mojom:mojom_platform_java", "//url:gurl_java",
diff --git a/android_webview/tools/automated_ui_tests/BUILD.gn b/android_webview/tools/automated_ui_tests/BUILD.gn index e8b6219..b641e52 100644 --- a/android_webview/tools/automated_ui_tests/BUILD.gn +++ b/android_webview/tools/automated_ui_tests/BUILD.gn
@@ -60,8 +60,8 @@ "//base:base_java_test_support", "//third_party/android_deps:espresso_java", "//third_party/android_sdk:android_test_base_java", - "//third_party/android_support_test_runner:rules_java", - "//third_party/android_support_test_runner:runner_java", + "//third_party/androidx:androidx_test_monitor_java", + "//third_party/androidx:androidx_test_rules_java", "//third_party/androidx:androidx_test_runner_java", "//third_party/androidx:androidx_test_uiautomator_uiautomator_java", "//third_party/hamcrest:hamcrest_java",
diff --git a/android_webview/tools/automated_ui_tests/javatests/src/org/chromium/webview_ui_test/test/ActionModeTest.java b/android_webview/tools/automated_ui_tests/javatests/src/org/chromium/webview_ui_test/test/ActionModeTest.java index 40dda2a2..2db2377 100644 --- a/android_webview/tools/automated_ui_tests/javatests/src/org/chromium/webview_ui_test/test/ActionModeTest.java +++ b/android_webview/tools/automated_ui_tests/javatests/src/org/chromium/webview_ui_test/test/ActionModeTest.java
@@ -42,9 +42,9 @@ import android.app.Instrumentation; import android.content.Intent; import android.os.Build; -import android.support.test.InstrumentationRegistry; import android.view.MenuItem; +import androidx.test.InstrumentationRegistry; import androidx.test.espresso.NoMatchingViewException; import androidx.test.espresso.PerformException; import androidx.test.espresso.Root;
diff --git a/android_webview/tools/automated_ui_tests/javatests/src/org/chromium/webview_ui_test/test/util/WebViewSyncWrapper.java b/android_webview/tools/automated_ui_tests/javatests/src/org/chromium/webview_ui_test/test/util/WebViewSyncWrapper.java index 049d8ab..d82840d5 100644 --- a/android_webview/tools/automated_ui_tests/javatests/src/org/chromium/webview_ui_test/test/util/WebViewSyncWrapper.java +++ b/android_webview/tools/automated_ui_tests/javatests/src/org/chromium/webview_ui_test/test/util/WebViewSyncWrapper.java
@@ -4,7 +4,7 @@ package org.chromium.webview_ui_test.test.util; -import static android.support.test.InstrumentationRegistry.getInstrumentation; +import static androidx.test.InstrumentationRegistry.getInstrumentation; import android.os.Looper; import android.webkit.ConsoleMessage;
diff --git a/android_webview/tools/system_webview_shell/BUILD.gn b/android_webview/tools/system_webview_shell/BUILD.gn index adad2289..5cf9c11 100644 --- a/android_webview/tools/system_webview_shell/BUILD.gn +++ b/android_webview/tools/system_webview_shell/BUILD.gn
@@ -125,8 +125,8 @@ "//base:base_java_test_support", "//content/public/android:content_java", "//content/public/test/android:content_java_test_support", - "//third_party/android_support_test_runner:rules_java", - "//third_party/android_support_test_runner:runner_java", + "//third_party/androidx:androidx_test_monitor_java", + "//third_party/androidx:androidx_test_rules_java", "//third_party/androidx:androidx_test_runner_java", "//third_party/junit", ] @@ -154,8 +154,8 @@ "//base:base_java", "//base:base_java_test_support", "//third_party/android_sdk:android_test_base_java", - "//third_party/android_support_test_runner:rules_java", - "//third_party/android_support_test_runner:runner_java", + "//third_party/androidx:androidx_test_monitor_java", + "//third_party/androidx:androidx_test_rules_java", "//third_party/androidx:androidx_test_runner_java", "//third_party/junit", ]
diff --git a/android_webview/tools/system_webview_shell/layout_tests/src/org/chromium/webview_shell/test/WebPlatformTestsActivityTest.java b/android_webview/tools/system_webview_shell/layout_tests/src/org/chromium/webview_shell/test/WebPlatformTestsActivityTest.java index 3840f5b..de6a972e 100644 --- a/android_webview/tools/system_webview_shell/layout_tests/src/org/chromium/webview_shell/test/WebPlatformTestsActivityTest.java +++ b/android_webview/tools/system_webview_shell/layout_tests/src/org/chromium/webview_shell/test/WebPlatformTestsActivityTest.java
@@ -7,9 +7,9 @@ import static org.chromium.base.test.util.ScalableTimeout.scaleTimeout; import android.os.Handler; -import android.support.test.InstrumentationRegistry; import android.webkit.WebView; +import androidx.test.InstrumentationRegistry; import androidx.test.filters.MediumTest; import org.junit.Assert;
diff --git a/android_webview/tools/system_webview_shell/layout_tests/src/org/chromium/webview_shell/test/WebViewLayoutTest.java b/android_webview/tools/system_webview_shell/layout_tests/src/org/chromium/webview_shell/test/WebViewLayoutTest.java index 5178782..1d25665 100644 --- a/android_webview/tools/system_webview_shell/layout_tests/src/org/chromium/webview_shell/test/WebViewLayoutTest.java +++ b/android_webview/tools/system_webview_shell/layout_tests/src/org/chromium/webview_shell/test/WebViewLayoutTest.java
@@ -5,8 +5,8 @@ package org.chromium.webview_shell.test; import android.os.Bundle; -import android.support.test.InstrumentationRegistry; +import androidx.test.InstrumentationRegistry; import androidx.test.filters.MediumTest; import junit.framework.ComparisonFailure;
diff --git a/android_webview/tools/system_webview_shell/page_cycler/src/org/chromium/webview_shell/page_cycler/PageCyclerTest.java b/android_webview/tools/system_webview_shell/page_cycler/src/org/chromium/webview_shell/page_cycler/PageCyclerTest.java index 0b0be3ca..b8f03fd 100644 --- a/android_webview/tools/system_webview_shell/page_cycler/src/org/chromium/webview_shell/page_cycler/PageCyclerTest.java +++ b/android_webview/tools/system_webview_shell/page_cycler/src/org/chromium/webview_shell/page_cycler/PageCyclerTest.java
@@ -4,11 +4,11 @@ package org.chromium.webview_shell.page_cycler; -import android.support.test.InstrumentationRegistry; import android.webkit.WebSettings; import android.webkit.WebView; import android.webkit.WebViewClient; +import androidx.test.InstrumentationRegistry; import androidx.test.filters.LargeTest; import org.junit.Assert;
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd index 2fec6d1..d8ae607 100644 --- a/ash/ash_strings.grd +++ b/ash/ash_strings.grd
@@ -4813,7 +4813,7 @@ </message> <message name="IDS_ASH_CALENDAR_JOIN_BUTTON_ACCESSIBLE_NAME" desc="The join meeting button accessible name on the event list item views."> - Hit enter to join event <ph name="event_summary">$1<ex>Lunch break</ex></ph> + Join <ph name="event_summary">$1<ex>Lunch break</ex></ph> </message> <message name="IDS_ASH_STATUS_TRAY_PROGRESS_BAR_ACCESSIBLE_NAME" desc="The accessible name for the progress bar shown in the status tray.">
diff --git a/ash/ash_strings_grd/IDS_ASH_CALENDAR_JOIN_BUTTON_ACCESSIBLE_NAME.png.sha1 b/ash/ash_strings_grd/IDS_ASH_CALENDAR_JOIN_BUTTON_ACCESSIBLE_NAME.png.sha1 index ed28750f..2c592260 100644 --- a/ash/ash_strings_grd/IDS_ASH_CALENDAR_JOIN_BUTTON_ACCESSIBLE_NAME.png.sha1 +++ b/ash/ash_strings_grd/IDS_ASH_CALENDAR_JOIN_BUTTON_ACCESSIBLE_NAME.png.sha1
@@ -1 +1 @@ -a897cbbf687238c380b5b0b3b641cc7c194fb4c8 \ No newline at end of file +9103ecfa3006ec73595cd039ff5fd52bd09a4165 \ No newline at end of file
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc index 910b5bc..9cc8deb2 100644 --- a/ash/constants/ash_features.cc +++ b/ash/constants/ash_features.cc
@@ -1185,7 +1185,7 @@ // Enable or disable a new header bar for the ChromeOS virtual keyboard. BASE_FEATURE(kVirtualKeyboardNewHeader, "VirtualKeyboardNewHeader", - base::FEATURE_DISABLED_BY_DEFAULT); + base::FEATURE_ENABLED_BY_DEFAULT); // If enabled, used to configure the heuristic rules for some advanced IME // features (e.g. auto-correct). @@ -2016,7 +2016,7 @@ // Enables the alternative emulator for the Terminal app. BASE_FEATURE(kTerminalAlternativeEmulator, "TerminalAlternativeEmulator", - base::FEATURE_DISABLED_BY_DEFAULT); + base::FEATURE_ENABLED_BY_DEFAULT); // // Enables Terminal System App to load from Downloads for developer testing. // Only works in dev and canary channels.
diff --git a/ash/shelf/drag_window_from_shelf_controller.cc b/ash/shelf/drag_window_from_shelf_controller.cc index 2463423..18f66604 100644 --- a/ash/shelf/drag_window_from_shelf_controller.cc +++ b/ash/shelf/drag_window_from_shelf_controller.cc
@@ -20,6 +20,7 @@ #include "ash/wallpaper/wallpaper_constants.h" #include "ash/wallpaper/wallpaper_view.h" #include "ash/wallpaper/wallpaper_widget_controller.h" +#include "ash/wm/desks/desks_util.h" #include "ash/wm/float/float_controller.h" #include "ash/wm/mru_window_tracker.h" #include "ash/wm/overview/overview_constants.h" @@ -242,8 +243,20 @@ other_window_copy_ = wm::RecreateLayers(other_window_); other_window_copy_->root()->SetVisible(true); other_window_copy_->root()->SetOpacity(1.f); - other_window_->layer()->parent()->StackAbove(other_window_copy_->root(), - other_window_->layer()); + + // If `other_window_` is the floated window, we need to move the copy to + // the active desk container. The float container will be moved under the + // desk containers (see `ScopedFloatContainerStacker `), so that the + // overview item does not appear above the dragged window during the drag. + if (other_window_ == floated_window) { + ui::Layer* new_parent = desks_util::GetActiveDeskContainerForRoot( + Shell::GetPrimaryRootWindow()) + ->layer(); + new_parent->Add(other_window_copy_->root()); + } else { + other_window_->layer()->parent()->StackAbove(other_window_copy_->root(), + other_window_->layer()); + } } } @@ -628,12 +641,16 @@ copy_scale = 1.f - base::clamp(copy_scale, 0.f, 1.f); other_window_copy_->root()->SetOpacity(copy_scale); - const float copy_transform_scale = - base::clamp(copy_scale, kOtherWindowMaxScale, 1.f); - const gfx::Transform copy_transform = gfx::GetScaleTransform( - other_window_copy_->root()->bounds().CenterPoint(), - copy_transform_scale); - other_window_copy_->root()->SetTransform(copy_transform); + + CHECK(other_window_); + if (!WindowState::Get(other_window_)->IsFloated()) { + const float copy_transform_scale = + base::clamp(copy_scale, kOtherWindowMaxScale, 1.f); + const gfx::Transform copy_transform = gfx::GetScaleTransform( + other_window_copy_->root()->bounds().CenterPoint(), + copy_transform_scale); + other_window_copy_->root()->SetTransform(copy_transform); + } } }
diff --git a/ash/shelf/drag_window_from_shelf_controller_unittest.cc b/ash/shelf/drag_window_from_shelf_controller_unittest.cc index bdecbff..5232dd3 100644 --- a/ash/shelf/drag_window_from_shelf_controller_unittest.cc +++ b/ash/shelf/drag_window_from_shelf_controller_unittest.cc
@@ -1488,12 +1488,13 @@ ui::Layer* other_window_copy_layer = GetOtherWindowCopyLayer(); ASSERT_TRUE(other_window_copy_layer); - // To check if the copy is of the floated window, we check the parent and - // bounds. - EXPECT_EQ(floated_window->layer()->parent(), - other_window_copy_layer->parent()); + // To check if the copy is of the floated window, we check the bounds. The + // float container gets stacked under the desk containers during overview, so + // the copy should be on a different parent. EXPECT_EQ(floated_window->layer()->bounds(), other_window_copy_layer->bounds()); + EXPECT_NE(floated_window->layer()->parent(), + other_window_copy_layer->parent()); Drag(gfx::Point(0, 200), 1.f, 1.f); EndDrag(shelf_bounds.CenterPoint(), /*velocity_y=*/absl::nullopt);
diff --git a/ash/style/ash_color_mixer.cc b/ash/style/ash_color_mixer.cc index c0822c1..17123b2 100644 --- a/ash/style/ash_color_mixer.cc +++ b/ash/style/ash_color_mixer.cc
@@ -242,18 +242,14 @@ cros_tokens::kCrosSysOnPrimaryContainer}; mixer[cros_tokens::kBgColor] = {cros_tokens::kCrosSysAppBase}; - mixer[cros_tokens::kBgColorElevation1] = { - cros_tokens::kCrosSysAppBaseElevated}; + mixer[cros_tokens::kBgColorElevation1] = {cros_tokens::kCrosSysBaseElevated}; mixer[cros_tokens::kBgColorElevation2Light] = { - cros_tokens::kCrosSysAppBaseElevatedLight}; + cros_tokens::kCrosSysBaseElevatedLight}; mixer[cros_tokens::kBgColorElevation2Dark] = { - cros_tokens::kCrosSysAppBaseElevatedDark}; - mixer[cros_tokens::kBgColorElevation3] = { - cros_tokens::kCrosSysAppBaseElevated}; - mixer[cros_tokens::kBgColorElevation4] = { - cros_tokens::kCrosSysAppBaseElevated}; - mixer[cros_tokens::kBgColorElevation5] = { - cros_tokens::kCrosSysAppBaseElevated}; + cros_tokens::kCrosSysBaseElevatedDark}; + mixer[cros_tokens::kBgColorElevation3] = {cros_tokens::kCrosSysBaseElevated}; + mixer[cros_tokens::kBgColorElevation4] = {cros_tokens::kCrosSysBaseElevated}; + mixer[cros_tokens::kBgColorElevation5] = {cros_tokens::kCrosSysBaseElevated}; mixer[cros_tokens::kBgColorDroppedElevation1] = { cros_tokens::kCrosSysAppBaseShaded}; mixer[cros_tokens::kBgColorDroppedElevation2] = { @@ -299,7 +295,7 @@ cros_tokens::kCrosSysRipplePrimary}; mixer[cros_tokens::kHighlightColor] = {cros_tokens::kCrosSysPrimary}; mixer[cros_tokens::kTextfieldBackgroundColor] = { - cros_tokens::kCrosSysInputFieldLight}; + cros_tokens::kCrosSysInputFieldOnShaded}; mixer[cros_tokens::kTextfieldLabelColor] = {cros_tokens::kCrosSysOnSurface}; mixer[cros_tokens::kSliderColorActive] = {cros_tokens::kCrosSysPrimary}; @@ -567,7 +563,7 @@ mixer[ui::kColorAshSystemUIMenuBackground] = { chromeos::features::IsJellyEnabled() - ? static_cast<ui::ColorId>(cros_tokens::kCrosSysAppBaseElevated) + ? static_cast<ui::ColorId>(cros_tokens::kCrosSysBaseElevated) : kColorAshShieldAndBase80}; mixer[ui::kColorAshSystemUIMenuIcon] = {kColorAshIconColorPrimary}; mixer[ui::kColorAshSystemUIMenuItemBackgroundSelected] = {kColorAshInkDrop};
diff --git a/ash/style/dark_light_mode_controller_impl.cc b/ash/style/dark_light_mode_controller_impl.cc index e105f8f..ecd65d0 100644 --- a/ash/style/dark_light_mode_controller_impl.cc +++ b/ash/style/dark_light_mode_controller_impl.cc
@@ -52,10 +52,14 @@ native_theme->NotifyOnNativeThemeUpdated(); auto* native_theme_web = ui::NativeTheme::GetInstanceForWeb(); - native_theme_web->set_use_dark_colors(is_dark_mode_enabled); - native_theme_web->set_preferred_color_scheme( - is_dark_mode_enabled ? ui::NativeTheme::PreferredColorScheme::kDark - : ui::NativeTheme::PreferredColorScheme::kLight); + if (!native_theme_web->IsForcedDarkMode()) { + // If we're in forced dark mode, leave the value alone to allow the tests to + // work. + native_theme_web->set_use_dark_colors(is_dark_mode_enabled); + native_theme_web->set_preferred_color_scheme( + is_dark_mode_enabled ? ui::NativeTheme::PreferredColorScheme::kDark + : ui::NativeTheme::PreferredColorScheme::kLight); + } native_theme_web->set_user_color(themed_color); native_theme_web->NotifyOnNativeThemeUpdated(); } @@ -152,9 +156,6 @@ } bool DarkLightModeControllerImpl::IsDarkModeEnabled() const { - if (!features::IsDarkLightModeEnabled() && override_light_mode_as_default_) - return false; - // Dark mode is off during OOBE when the OobeDialogState is still unknown. // When the SessionState is OOBE, the OobeDialogState is HIDDEN until the // first screen is shown. This fixes a bug that caused dark colors to be @@ -166,51 +167,54 @@ return false; } - if (features::IsDarkLightModeEnabled()) { - if (is_dark_mode_enabled_in_oobe_for_testing_.has_value()) - return is_dark_mode_enabled_in_oobe_for_testing_.value(); + if (is_dark_mode_enabled_in_oobe_for_testing_.has_value()) { + return is_dark_mode_enabled_in_oobe_for_testing_.value(); + } - if (oobe_state_ != OobeDialogState::HIDDEN) { - if (active_user_pref_service_) { - const PrefService::Preference* pref = - active_user_pref_service_->FindPreference( - prefs::kDarkModeScheduleType); - // Managed users do not see the theme selection screen, so to avoid - // confusion they should always see light colors during OOBE - if (pref->IsManaged() || pref->IsRecommended()) - return false; - - if (!active_user_pref_service_->GetBoolean(prefs::kDarkModeEnabled)) - return false; + if (oobe_state_ != OobeDialogState::HIDDEN) { + if (active_user_pref_service_) { + const PrefService::Preference* pref = + active_user_pref_service_->FindPreference( + prefs::kDarkModeScheduleType); + // Managed users do not see the theme selection screen, so to avoid + // confusion they should always see light colors during OOBE + if (pref->IsManaged() || pref->IsRecommended()) { + return false; } - return base::Contains(kStatesSupportingDarkTheme, oobe_state_); - } - // On the login screen use the preference of the focused pod's user if they - // had the preference stored in the known_user and the pod is focused. - if (!active_user_pref_service_ && - is_dark_mode_enabled_for_focused_pod_.has_value()) { - return is_dark_mode_enabled_for_focused_pod_.value(); + if (!active_user_pref_service_->GetBoolean(prefs::kDarkModeEnabled)) { + return false; + } } + return base::Contains(kStatesSupportingDarkTheme, oobe_state_); + } + + // On the login screen use the preference of the focused pod's user if they + // had the preference stored in the known_user and the pod is focused. + if (!active_user_pref_service_ && + is_dark_mode_enabled_for_focused_pod_.has_value()) { + return is_dark_mode_enabled_for_focused_pod_.value(); } // Keep the color mode as DARK in login screen or when dark/light mode feature // is not enabled. - if (!active_user_pref_service_ || !features::IsDarkLightModeEnabled()) + if (!active_user_pref_service_) { return true; + } return active_user_pref_service_->GetBoolean(prefs::kDarkModeEnabled); } void DarkLightModeControllerImpl::SetDarkModeEnabledForTest(bool enabled) { - DCHECK(features::IsDarkLightModeEnabled()); if (oobe_state_ != OobeDialogState::HIDDEN) { auto closure = GetNotifyOnDarkModeChangeClosure(); is_dark_mode_enabled_in_oobe_for_testing_ = enabled; return; } - if (IsDarkModeEnabled() != enabled) + + if (IsDarkModeEnabled() != enabled) { ToggleColorMode(); + } } void DarkLightModeControllerImpl::OnOobeDialogStateChanged( @@ -232,17 +236,11 @@ } void DarkLightModeControllerImpl::OnWallpaperColorsChanged() { - if (!features::IsDarkLightModeEnabled()) - return; - RefreshColorsOnColorMode(IsDarkModeEnabled()); } void DarkLightModeControllerImpl::OnActiveUserPrefServiceChanged( PrefService* prefs) { - if (!features::IsDarkLightModeEnabled()) - return; - active_user_pref_service_ = prefs; pref_change_registrar_ = std::make_unique<PrefChangeRegistrar>(); pref_change_registrar_->Init(prefs); @@ -260,8 +258,6 @@ void DarkLightModeControllerImpl::OnSessionStateChanged( session_manager::SessionState state) { - if (!features::IsDarkLightModeEnabled()) - return; if (state != session_manager::SessionState::OOBE && state != session_manager::SessionState::LOGIN_PRIMARY) { oobe_state_ = OobeDialogState::HIDDEN;
diff --git a/ash/style/dark_light_mode_controller_unittests.cc b/ash/style/dark_light_mode_controller_unittests.cc index ff2d3c03..3c0c974 100644 --- a/ash/style/dark_light_mode_controller_unittests.cc +++ b/ash/style/dark_light_mode_controller_unittests.cc
@@ -4,7 +4,6 @@ #include "ash/style/dark_light_mode_controller_impl.h" -#include "ash/constants/ash_features.h" #include "ash/login/login_screen_controller.h" #include "ash/login/ui/login_test_base.h" #include "ash/public/cpp/login_types.h" @@ -24,8 +23,6 @@ auto* client = GetSessionControllerClient(); auto* dark_light_mode_controller = DarkLightModeControllerImpl::Get(); - base::test::ScopedFeatureList enable_dark_light; - enable_dark_light.InitAndEnableFeature(chromeos::features::kDarkLightMode); client->SetSessionState(session_manager::SessionState::UNKNOWN); // When dark/light mode is enabled. Color mode in non-active user sessions // (e.g, login page) should be DARK. @@ -46,18 +43,6 @@ dispatcher->NotifyOobeDialogState(OobeDialogState::GAIA_SIGNIN); EXPECT_FALSE(dark_light_mode_controller->IsDarkModeEnabled()); - - // When dark/light mode is disabled. Color mode in non-active user sessions - // (e.g, login page) should still be DARK. - base::test::ScopedFeatureList disable_dark_light; - disable_dark_light.InitWithFeatures( - /*enabled_features=*/{}, /*disabled_features=*/{ - chromeos::features::kDarkLightMode, features::kNotificationsRefresh}); - client->SetSessionState(session_manager::SessionState::UNKNOWN); - EXPECT_TRUE(dark_light_mode_controller->IsDarkModeEnabled()); - client->SetSessionState(session_manager::SessionState::OOBE); - dispatcher->NotifyOobeDialogState(OobeDialogState::USER_CREATION); - EXPECT_TRUE(dark_light_mode_controller->IsDarkModeEnabled()); } } // namespace ash
diff --git a/ash/system/input_device_settings/input_device_settings_metrics_manager.cc b/ash/system/input_device_settings/input_device_settings_metrics_manager.cc index 53b406a..652ae4b8 100644 --- a/ash/system/input_device_settings/input_device_settings_metrics_manager.cc +++ b/ash/system/input_device_settings/input_device_settings_metrics_manager.cc
@@ -5,6 +5,8 @@ #include "ash/system/input_device_settings/input_device_settings_metrics_manager.h" #include "ash/public/mojom/input_device_settings.mojom-forward.h" +#include "ash/session/session_controller_impl.h" +#include "ash/shell.h" #include "base/metrics/histogram_functions.h" namespace ash { @@ -37,7 +39,17 @@ void InputDeviceSettingsMetricsManager::RecordKeyboardInitialMetrics( const mojom::Keyboard& keyboard) { - // TODO(yyhyyh@): Only record the metrics once for each keyboard. + // Only record the metrics once for each keyboard. + const auto account_id = + Shell::Get()->session_controller()->GetActiveAccountId(); + auto iter = recorded_keyboards_.find(account_id); + + if (iter != recorded_keyboards_.end() && + base::Contains(iter->second, keyboard.id)) { + return; + } + recorded_keyboards_[account_id].insert(keyboard.id); + const KeyboardType keyboard_type = GetKeyboardType(keyboard); switch (keyboard_type) { case KeyboardType::kExternal:
diff --git a/ash/system/input_device_settings/input_device_settings_metrics_manager.h b/ash/system/input_device_settings/input_device_settings_metrics_manager.h index 1ff9e4d..e41df29 100644 --- a/ash/system/input_device_settings/input_device_settings_metrics_manager.h +++ b/ash/system/input_device_settings/input_device_settings_metrics_manager.h
@@ -7,6 +7,9 @@ #include "ash/ash_export.h" #include "ash/public/mojom/input_device_settings.mojom.h" +#include "base/containers/flat_map.h" +#include "base/containers/flat_set.h" +#include "components/account_id/account_id.h" namespace ash { @@ -20,6 +23,9 @@ ~InputDeviceSettingsMetricsManager(); void RecordKeyboardInitialMetrics(const mojom::Keyboard& keyboard); + + private: + base::flat_map<AccountId, base::flat_set<uint32_t>> recorded_keyboards_; }; } // namespace ash
diff --git a/ash/system/input_device_settings/input_device_settings_metrics_manager_unittest.cc b/ash/system/input_device_settings/input_device_settings_metrics_manager_unittest.cc index 2d52eae..a065d2c1 100644 --- a/ash/system/input_device_settings/input_device_settings_metrics_manager_unittest.cc +++ b/ash/system/input_device_settings/input_device_settings_metrics_manager_unittest.cc
@@ -15,6 +15,9 @@ constexpr int kExternalKeyboardId = 1; constexpr int kExternalChromeOSKeyboardId = 2; constexpr int kInternalKeyboardId = 3; + +constexpr char kUser1[] = "user1@gmail.com"; +constexpr char kUser2[] = "user2@gmail.com"; } // namespace class InputDeviceSettingsMetricsManagerTest : public AshTestBase { @@ -107,4 +110,48 @@ /*expected_count=*/1u); } +TEST_F(InputDeviceSettingsMetricsManagerTest, RecordMetricOncePerKeyboard) { + mojom::Keyboard keyboard_external; + keyboard_external.id = kExternalKeyboardId; + keyboard_external.is_external = true; + keyboard_external.meta_key = mojom::MetaKey::kCommand; + keyboard_external.settings = mojom::KeyboardSettings::New(); + auto& settings_external = *keyboard_external.settings; + settings_external.top_row_are_fkeys = true; + + mojom::Keyboard keyboard_internal; + keyboard_internal.id = kInternalKeyboardId; + keyboard_internal.is_external = false; + keyboard_internal.settings = mojom::KeyboardSettings::New(); + auto& settings_internal = *keyboard_internal.settings; + settings_internal.top_row_are_fkeys = true; + + base::HistogramTester histogram_tester; + SimulateUserLogin(kUser1); + manager_.get()->RecordKeyboardInitialMetrics(keyboard_external); + histogram_tester.ExpectTotalCount( + "ChromeOS.Settings.Device.Keyboard.External.TopRowAreFKeys.Initial", + /*expected_count=*/1u); + + manager_.get()->RecordKeyboardInitialMetrics(keyboard_internal); + histogram_tester.ExpectTotalCount( + "ChromeOS.Settings.Device.Keyboard.Internal.TopRowAreFKeys.Initial", + /*expected_count=*/1u); + + // Call RecordKeyboardInitialMetrics with the same user and same keyboard, + // ExpectTotalCount for Internal metric won't increase. + manager_.get()->RecordKeyboardInitialMetrics(keyboard_internal); + histogram_tester.ExpectTotalCount( + "ChromeOS.Settings.Device.Keyboard.Internal.TopRowAreFKeys.Initial", + /*expected_count=*/1u); + + // Call RecordKeyboardInitialMetrics with the different user but same + // keyboard, ExpectTotalCount for Internal metric will increase. + SimulateUserLogin(kUser2); + manager_.get()->RecordKeyboardInitialMetrics(keyboard_internal); + histogram_tester.ExpectTotalCount( + "ChromeOS.Settings.Device.Keyboard.Internal.TopRowAreFKeys.Initial", + /*expected_count=*/2u); +} + } // namespace ash \ No newline at end of file
diff --git a/ash/touch/touch_selection_magnifier_runner_ash.cc b/ash/touch/touch_selection_magnifier_runner_ash.cc index c48244b3..1303fe0 100644 --- a/ash/touch/touch_selection_magnifier_runner_ash.cc +++ b/ash/touch/touch_selection_magnifier_runner_ash.cc
@@ -4,28 +4,76 @@ #include "ash/touch/touch_selection_magnifier_runner_ash.h" -#include "ash/constants/ash_features.h" #include "ash/public/cpp/shell_window_ids.h" +#include "ash/style/color_util.h" +#include "third_party/skia/include/core/SkDrawLooper.h" #include "ui/aura/window.h" +#include "ui/chromeos/styles/cros_tokens_color_mappings.h" +#include "ui/color/color_provider_source_observer.h" #include "ui/compositor/layer.h" +#include "ui/compositor/paint_recorder.h" +#include "ui/compositor/scoped_layer_animation_settings.h" #include "ui/gfx/geometry/point_conversions.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/size.h" +#include "ui/gfx/shadow_value.h" +#include "ui/gfx/skia_paint_util.h" namespace ash { namespace { -// Gets the bounds of the magnifier when showing the specified point of -// interest. `point_of_interest` and returned bounds are in root window -// coordinates. -gfx::Rect GetBounds(const gfx::Point& point_of_interest) { - const gfx::Size size = TouchSelectionMagnifierRunnerAsh::kMagnifierLayerSize; +constexpr int kMagnifierRadius = 20; + +constexpr int kMagnifierBorderThickness = 1; + +const gfx::ShadowValues kMagnifierShadowValues = + gfx::ShadowValue::MakeChromeOSSystemUIShadowValues(3); + +// The space outside the zoom layer needed for shadows. +const gfx::Outsets kMagnifierShadowOutsets = + gfx::ShadowValue::GetMargin(kMagnifierShadowValues).ToOutsets(); + +// Bounds of the zoom layer in coordinates of its parent. These zoom layer +// bounds are fixed since we only update the bounds of the parent magnifier +// layer when the magnifier moves. +const gfx::Rect kZoomLayerBounds = + gfx::Rect(kMagnifierShadowOutsets.left(), + kMagnifierShadowOutsets.top(), + TouchSelectionMagnifierRunnerAsh::kMagnifierSize.width(), + TouchSelectionMagnifierRunnerAsh::kMagnifierSize.height()); + +// Size of the border layer, which includes space for the zoom layer and +// surrounding border and shadows. +const gfx::Size kBorderLayerSize = + TouchSelectionMagnifierRunnerAsh::kMagnifierSize + + kMagnifierShadowOutsets.size(); + +// Duration of the animation when updating magnifier bounds. +constexpr base::TimeDelta kMagnifierTransitionDuration = base::Milliseconds(50); + +// Gets the bounds of the magnifier layer for showing the specified point of +// interest. These bounds include the magnifier border and shadows. +// `point_of_interest` and returned bounds are in coordinates of the magnifier's +// parent container. +gfx::Rect GetMagnifierLayerBounds(const gfx::Point& point_of_interest) { + const gfx::Size& size = TouchSelectionMagnifierRunnerAsh::kMagnifierSize; const gfx::Point origin( point_of_interest.x() - size.width() / 2, point_of_interest.y() - size.height() / 2 + TouchSelectionMagnifierRunnerAsh::kMagnifierVerticalOffset); - return gfx::Rect(origin, size); + gfx::Rect magnifier_layer_bounds(origin, size); + magnifier_layer_bounds.Outset(kMagnifierShadowOutsets); + return magnifier_layer_bounds; +} + +// Gets the border color using `color_provider_source`. Defaults to black if +// `color_provider_source` is nullptr. +SkColor GetBorderColor(const ui::ColorProviderSource* color_provider_source) { + return color_provider_source + ? color_provider_source->GetColorProvider()->GetColor( + cros_tokens::kCrosSysSeparator) + : SkColorSetARGB(51, 0, 0, 0); } // Returns the child container in `root` that should parent the magnifier layer. @@ -35,6 +83,59 @@ } // namespace +// Delegate for drawing the magnifier border and shadows onto the border layer. +class TouchSelectionMagnifierRunnerAsh::BorderRenderer + : public ui::LayerDelegate { + public: + explicit BorderRenderer(SkColor border_color) : border_color_(border_color) {} + + BorderRenderer(const BorderRenderer&) = delete; + BorderRenderer& operator=(const BorderRenderer&) = delete; + ~BorderRenderer() override = default; + + void set_border_color(SkColor border_color) { border_color_ = border_color; } + + // ui::LayerDelegate: + void OnPaintLayer(const ui::PaintContext& context) override { + ui::PaintRecorder recorder(context, kBorderLayerSize); + + // Draw shadows onto the border layer. These shadows should surround the + // magnified area, so we draw them around the zoom layer bounds. + cc::PaintFlags shadow_flags; + shadow_flags.setAntiAlias(true); + shadow_flags.setColor(SK_ColorTRANSPARENT); + shadow_flags.setLooper(gfx::CreateShadowDrawLooper(kMagnifierShadowValues)); + recorder.canvas()->DrawRoundRect(kZoomLayerBounds, kMagnifierRadius, + shadow_flags); + + // Since the border layer is stacked above the zoom layer (to prevent the + // magnifier border and shadows from being magnified), we now need to clear + // the parts of the shadow covering the zoom layer. + cc::PaintFlags mask_flags; + mask_flags.setAntiAlias(true); + mask_flags.setBlendMode(SkBlendMode::kClear); + mask_flags.setStyle(cc::PaintFlags::kFill_Style); + recorder.canvas()->DrawRoundRect(kZoomLayerBounds, kMagnifierRadius, + mask_flags); + + // Draw the magnifier border onto the border layer, using the zoom layer + // bounds so that the border surrounds the magnified area. + cc::PaintFlags border_flags; + border_flags.setAntiAlias(true); + border_flags.setStyle(cc::PaintFlags::kStroke_Style); + border_flags.setStrokeWidth(kMagnifierBorderThickness); + border_flags.setColor(border_color_); + recorder.canvas()->DrawRoundRect(kZoomLayerBounds, kMagnifierRadius, + border_flags); + } + + void OnDeviceScaleFactorChanged(float old_device_scale_factor, + float new_device_scale_factor) override {} + + private: + SkColor border_color_; +}; + TouchSelectionMagnifierRunnerAsh::TouchSelectionMagnifierRunnerAsh() = default; TouchSelectionMagnifierRunnerAsh::~TouchSelectionMagnifierRunnerAsh() = default; @@ -50,26 +151,47 @@ aura::Window* root_window = current_context_->GetRootWindow(); DCHECK(root_window); - gfx::PointF position_in_root(position); - aura::Window::ConvertPointToTarget(context, root_window, &position_in_root); + aura::Window* parent_container = + GetMagnifierParentContainerForRoot(root_window); + gfx::PointF position_in_parent(position); + aura::Window::ConvertPointToTarget(context, parent_container, + &position_in_parent); if (!magnifier_layer_) { - CreateMagnifierLayer(root_window, position_in_root); + CreateMagnifierLayer(parent_container, position_in_parent); } else { + ui::ScopedLayerAnimationSettings settings(magnifier_layer_->GetAnimator()); + settings.SetTransitionDuration(kMagnifierTransitionDuration); + settings.SetTweenType(gfx::Tween::LINEAR); + settings.SetPreemptionStrategy( + ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); magnifier_layer_->SetBounds( - GetBounds(gfx::ToRoundedPoint(position_in_root))); + GetMagnifierLayerBounds(gfx::ToRoundedPoint(position_in_parent))); } } void TouchSelectionMagnifierRunnerAsh::CloseMagnifier() { current_context_ = nullptr; magnifier_layer_ = nullptr; + zoom_layer_ = nullptr; + border_layer_ = nullptr; + border_renderer_ = nullptr; + Observe(nullptr); } bool TouchSelectionMagnifierRunnerAsh::IsRunning() const { return current_context_ != nullptr; } +void TouchSelectionMagnifierRunnerAsh::OnColorProviderChanged() { + if (border_renderer_) { + DCHECK(border_layer_); + border_renderer_->set_border_color( + GetBorderColor(GetColorProviderSource())); + border_layer_->SchedulePaint(gfx::Rect(border_layer_->size())); + } +} + const aura::Window* TouchSelectionMagnifierRunnerAsh::GetCurrentContextForTesting() const { return current_context_; @@ -80,21 +202,44 @@ return magnifier_layer_.get(); } -void TouchSelectionMagnifierRunnerAsh::CreateMagnifierLayer( - aura::Window* root_window, - const gfx::PointF& position_in_root) { - aura::Window* parent_container = - GetMagnifierParentContainerForRoot(root_window); +const ui::Layer* TouchSelectionMagnifierRunnerAsh::GetZoomLayerForTesting() + const { + return zoom_layer_.get(); +} +void TouchSelectionMagnifierRunnerAsh::CreateMagnifierLayer( + aura::Window* parent_container, + const gfx::PointF& position_in_parent) { + Observe(ColorUtil::GetColorProviderSourceForWindow(parent_container)); ui::Layer* parent_layer = parent_container->layer(); - magnifier_layer_ = std::make_unique<ui::Layer>(ui::LAYER_SOLID_COLOR); - magnifier_layer_->SetBounds(GetBounds(gfx::ToRoundedPoint(position_in_root))); - magnifier_layer_->SetBackgroundZoom(kMagnifierScale, 0); - magnifier_layer_->SetBackgroundOffset( - gfx::Point(0, kMagnifierVerticalOffset)); + + // Create the magnifier layer, which will parent the zoom layer and border + // layer. + magnifier_layer_ = std::make_unique<ui::Layer>(ui::LAYER_NOT_DRAWN); + magnifier_layer_->SetBounds( + GetMagnifierLayerBounds(gfx::ToRoundedPoint(position_in_parent))); magnifier_layer_->SetFillsBoundsOpaquely(false); - magnifier_layer_->SetRoundedCornerRadius(kMagnifierRoundedCorners); parent_layer->Add(magnifier_layer_.get()); + + // Create the zoom layer, which will show the magnified area. + zoom_layer_ = std::make_unique<ui::Layer>(ui::LAYER_SOLID_COLOR); + zoom_layer_->SetBounds(kZoomLayerBounds); + zoom_layer_->SetBackgroundZoom(kMagnifierScale, 0); + zoom_layer_->SetBackgroundOffset(gfx::Point(0, kMagnifierVerticalOffset)); + zoom_layer_->SetFillsBoundsOpaquely(false); + zoom_layer_->SetRoundedCornerRadius(gfx::RoundedCornersF{kMagnifierRadius}); + magnifier_layer_->Add(zoom_layer_.get()); + + // Create the border layer. This is stacked above the zoom layer so that the + // magnifier border and shadows aren't shown in the magnified area drawn by + // the zoom layer. + border_layer_ = std::make_unique<ui::Layer>(); + border_layer_->SetBounds(gfx::Rect(kBorderLayerSize)); + border_renderer_ = std::make_unique<BorderRenderer>( + GetBorderColor(GetColorProviderSource())); + border_layer_->set_delegate(border_renderer_.get()); + border_layer_->SetFillsBoundsOpaquely(false); + magnifier_layer_->Add(border_layer_.get()); } } // namespace ash
diff --git a/ash/touch/touch_selection_magnifier_runner_ash.h b/ash/touch/touch_selection_magnifier_runner_ash.h index 7f5a5ab..a5e3bf0 100644 --- a/ash/touch/touch_selection_magnifier_runner_ash.h +++ b/ash/touch/touch_selection_magnifier_runner_ash.h
@@ -7,7 +7,7 @@ #include "ash/ash_export.h" #include "base/memory/raw_ptr.h" -#include "ui/gfx/geometry/rounded_corners_f.h" +#include "ui/color/color_provider_source_observer.h" #include "ui/gfx/geometry/size.h" #include "ui/touch_selection/touch_selection_magnifier_runner.h" @@ -23,7 +23,8 @@ // Ash implementation for TouchSelectionMagnifierRunner. class ASH_EXPORT TouchSelectionMagnifierRunnerAsh - : public ui::TouchSelectionMagnifierRunner { + : public ui::TouchSelectionMagnifierRunner, + public ui::ColorProviderSourceObserver { public: TouchSelectionMagnifierRunnerAsh(); @@ -34,15 +35,14 @@ ~TouchSelectionMagnifierRunnerAsh() override; - static constexpr float kMagnifierScale = 2.0f; + static constexpr float kMagnifierScale = 1.25f; - static constexpr gfx::Size kMagnifierLayerSize{100, 48}; - - static constexpr gfx::RoundedCornersF kMagnifierRoundedCorners{20}; + // Size of the magnifier zoom layer, which excludes border and shadows. + static constexpr gfx::Size kMagnifierSize{100, 40}; // Offset to apply so that the magnifier is shown vertically above the point // of interest. The offset specifies vertical displacement from the center of - // the text selection caret to the center of the magnifier bounds. + // the text selection caret to the center of the magnifier zoom layer. static constexpr int kMagnifierVerticalOffset = -32; // ui::TouchSelectionMagnifierRunner: @@ -51,20 +51,37 @@ void CloseMagnifier() override; bool IsRunning() const override; + // ui::ColorProviderSourceObserver: + void OnColorProviderChanged() override; + const aura::Window* GetCurrentContextForTesting() const; const ui::Layer* GetMagnifierLayerForTesting() const; + const ui::Layer* GetZoomLayerForTesting() const; + private: - void CreateMagnifierLayer(aura::Window* root_window, + class BorderRenderer; + + void CreateMagnifierLayer(aura::Window* parent_container, const gfx::PointF& position_in_root); // Current context window in which the magnifier is being shown, or `nullptr` // if no magnifier is running. raw_ptr<aura::Window> current_context_ = nullptr; - // The magnifier layer, which draws the background with a zoom filter applied. + // The magnifier layer is the parent of the zoom layer and border layer. The + // layer bounds should be updated when selection updates occur. std::unique_ptr<ui::Layer> magnifier_layer_; + + // Draws the background with a zoom filter applied. + std::unique_ptr<ui::Layer> zoom_layer_; + + // Draws a border and shadow. `border_layer_` must be ordered after + // `border_renderer_` so that it is destroyed before `border_renderer_`. + // Otherwise `border_layer_` will have a pointer to a deleted delegate. + std::unique_ptr<BorderRenderer> border_renderer_; + std::unique_ptr<ui::Layer> border_layer_; }; } // namespace ash
diff --git a/ash/touch/touch_selection_magnifier_runner_ash_unittest.cc b/ash/touch/touch_selection_magnifier_runner_ash_unittest.cc index 56f88f8..d789003 100644 --- a/ash/touch/touch_selection_magnifier_runner_ash_unittest.cc +++ b/ash/touch/touch_selection_magnifier_runner_ash_unittest.cc
@@ -43,6 +43,34 @@ NoSessionAshTestBase::SetUp(); } + // Verifies that the magnifier has the correct bounds given the point of + // interest in context window coordinates. + // TODO(b/273613374): For now, we assume that the context window, root + // window, and magnifier parent container have the same bounds, but in + // practice this might not always be the case. Rewrite these tests once the + // bounds related parts of the magnifier have been cleaned up. + void VerifyMagnifierBounds(gfx::PointF point_of_interest) { + TouchSelectionMagnifierRunnerAsh* magnifier_runner = GetMagnifierRunner(); + ASSERT_TRUE(magnifier_runner); + + const ui::Layer* magnifier_layer = + magnifier_runner->GetMagnifierLayerForTesting(); + const ui::Layer* zoom_layer = magnifier_runner->GetZoomLayerForTesting(); + ASSERT_TRUE(magnifier_layer); + ASSERT_TRUE(zoom_layer); + + gfx::Rect zoom_layer_bounds_in_context = + zoom_layer->bounds() + magnifier_layer->bounds().OffsetFromOrigin(); + EXPECT_EQ(zoom_layer_bounds_in_context.size(), + TouchSelectionMagnifierRunnerAsh::kMagnifierSize); + EXPECT_EQ( + zoom_layer_bounds_in_context.CenterPoint(), + gfx::Point( + point_of_interest.x(), + point_of_interest.y() + + TouchSelectionMagnifierRunnerAsh::kMagnifierVerticalOffset)); + } + private: base::test::ScopedFeatureList scoped_feature_list_; }; @@ -106,79 +134,34 @@ EXPECT_FALSE(magnifier_runner->GetCurrentContextForTesting()); } -// Tests that the magnifier layer is created and destroyed. -TEST_F(TouchSelectionMagnifierRunnerAshTest, Layer) { +// Tests that the magnifier and zoom layers are created and destroyed. +TEST_F(TouchSelectionMagnifierRunnerAshTest, CreatesAndDestroysLayers) { TouchSelectionMagnifierRunnerAsh* magnifier_runner = GetMagnifierRunner(); ASSERT_TRUE(magnifier_runner); magnifier_runner->ShowMagnifier(GetContext(), gfx::PointF(300, 200)); ASSERT_TRUE(magnifier_runner->GetMagnifierLayerForTesting()); + ASSERT_TRUE(magnifier_runner->GetZoomLayerForTesting()); magnifier_runner->CloseMagnifier(); RunPendingMessages(); EXPECT_FALSE(magnifier_runner->GetMagnifierLayerForTesting()); + EXPECT_FALSE(magnifier_runner->GetZoomLayerForTesting()); } -// Tests that the magnifier layer is positioned with the correct bounds. -TEST_F(TouchSelectionMagnifierRunnerAshTest, LayerBounds) { +// Tests that the magnifier bounds are set correctly. +TEST_F(TouchSelectionMagnifierRunnerAshTest, CorrectBounds) { TouchSelectionMagnifierRunnerAsh* magnifier_runner = GetMagnifierRunner(); ASSERT_TRUE(magnifier_runner); gfx::PointF position(300, 200); magnifier_runner->ShowMagnifier(GetContext(), position); - const ui::Layer* magnifier_layer = - magnifier_runner->GetMagnifierLayerForTesting(); - ASSERT_TRUE(magnifier_layer); - - gfx::Rect bounds = magnifier_layer->bounds(); - EXPECT_EQ(bounds.size(), - TouchSelectionMagnifierRunnerAsh::kMagnifierLayerSize); - EXPECT_EQ( - bounds.CenterPoint(), - gfx::Point( - position.x(), - position.y() + - TouchSelectionMagnifierRunnerAsh::kMagnifierVerticalOffset)); - - magnifier_runner->CloseMagnifier(); - RunPendingMessages(); -} - -// Tests that the magnifier layer bounds update correctly. -TEST_F(TouchSelectionMagnifierRunnerAshTest, LayerUpdatesBounds) { - TouchSelectionMagnifierRunnerAsh* magnifier_runner = GetMagnifierRunner(); - ASSERT_TRUE(magnifier_runner); - - gfx::PointF position(300, 200); - magnifier_runner->ShowMagnifier(GetContext(), position); - const ui::Layer* magnifier_layer = - magnifier_runner->GetMagnifierLayerForTesting(); - ASSERT_TRUE(magnifier_layer); - - gfx::Rect bounds = magnifier_layer->bounds(); - EXPECT_EQ(bounds.size(), - TouchSelectionMagnifierRunnerAsh::kMagnifierLayerSize); - EXPECT_EQ( - bounds.CenterPoint(), - gfx::Point( - position.x(), - position.y() + - TouchSelectionMagnifierRunnerAsh::kMagnifierVerticalOffset)); + VerifyMagnifierBounds(position); // Move the magnifier. position = gfx::PointF(400, 150); magnifier_runner->ShowMagnifier(GetContext(), position); - EXPECT_EQ(magnifier_layer, magnifier_runner->GetMagnifierLayerForTesting()); - - bounds = magnifier_layer->bounds(); - EXPECT_EQ(bounds.size(), - TouchSelectionMagnifierRunnerAsh::kMagnifierLayerSize); - EXPECT_EQ( - bounds.CenterPoint(), - gfx::Point( - position.x(), - position.y() + - TouchSelectionMagnifierRunnerAsh::kMagnifierVerticalOffset)); + VerifyMagnifierBounds(position); magnifier_runner->CloseMagnifier(); RunPendingMessages();
diff --git a/ash/webui/personalization_app/.gitignore b/ash/webui/personalization_app/.gitignore new file mode 100644 index 0000000..e6d297d --- /dev/null +++ b/ash/webui/personalization_app/.gitignore
@@ -0,0 +1,2 @@ +# Do not check in until they are ready to be made public. +resources/time_of_day/*
diff --git a/ash/wm/float/float_controller_unittest.cc b/ash/wm/float/float_controller_unittest.cc index 1ae16e60..234ce02 100644 --- a/ash/wm/float/float_controller_unittest.cc +++ b/ash/wm/float/float_controller_unittest.cc
@@ -1429,6 +1429,28 @@ EXPECT_TRUE(window->IsVisible()); } +// Tests that the floated window is visible when it is untucked. +TEST_F(TabletWindowFloatTest, UntuckedWindowVisibility) { + Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true); + std::unique_ptr<aura::Window> window = CreateFloatedWindow(); + + // Long duration for tuck animation, to allow it to be interrupted. + ui::ScopedAnimationDurationScaleMode test_duration( + ui::ScopedAnimationDurationScaleMode::SLOW_DURATION); + + // Fling to tuck the window. Press the untuck handle before the window + // finishes the tuck animation. Test that the window is visible. + FlingWindow(window.get(), /*left=*/false, /*up=*/false); + auto* float_controller = Shell::Get()->float_controller(); + views::Widget* tuck_handle_widget = + float_controller->GetTuckHandleWidget(window.get()); + ASSERT_TRUE(tuck_handle_widget); + GetEventGenerator()->GestureTapAt( + tuck_handle_widget->GetWindowBoundsInScreen().CenterPoint()); + ASSERT_TRUE(window->IsVisible()); + EXPECT_FALSE(float_controller->IsFloatedWindowTuckedForTablet(window.get())); +} + // Tests that the expected window gets activation after tucking a floated // window, and that on untucking the floated window, it gains activation. TEST_F(TabletWindowFloatTest, WindowActivationAfterTuckingUntucking) {
diff --git a/ash/wm/float/scoped_window_tucker.cc b/ash/wm/float/scoped_window_tucker.cc index 7ed5d706..f8537f091 100644 --- a/ash/wm/float/scoped_window_tucker.cc +++ b/ash/wm/float/scoped_window_tucker.cc
@@ -247,8 +247,6 @@ left_ ? -kTuckOffscreenPaddingDp : kTuckOffscreenPaddingDp, 0); views::AnimationBuilder() - .OnAborted(base::BindOnce(&ScopedWindowTucker::OnAnimateTuckEnded, - weak_factory_.GetWeakPtr())) .OnEnded(base::BindOnce(&ScopedWindowTucker::OnAnimateTuckEnded, weak_factory_.GetWeakPtr())) .SetPreemptionStrategy(
diff --git a/ash/wm/snap_group/snap_group.cc b/ash/wm/snap_group/snap_group.cc index 70ee667..e48c60c2 100644 --- a/ash/wm/snap_group/snap_group.cc +++ b/ash/wm/snap_group/snap_group.cc
@@ -21,6 +21,19 @@ return snap_group_controller && snap_group_controller->IsArm1AutomaticallyLockEnabled(); } + +bool IsStackedBelow(aura::Window* win1, aura::Window* win2) { + DCHECK_NE(win1, win2); + DCHECK_EQ(win1->parent(), win2->parent()); + + const auto& children = win1->parent()->children(); + auto win1_iter = base::ranges::find(children, win1); + auto win2_iter = base::ranges::find(children, win2); + DCHECK(win1_iter != children.end()); + DCHECK(win2_iter != children.end()); + return win1_iter < win2_iter; +} + } // namespace SnapGroup::SnapGroup(aura::Window* window1, aura::Window* window2) { @@ -31,6 +44,32 @@ StopObservingWindows(); } +void SnapGroup::OnWindowStackingChanged(aura::Window* window) { + // Update the stacking order of the other window in the snap group so that the + // two windows are always placed on top, both of which will be stacked below + // the `split_view_divider` if applicable afterwards. + aura::Window* top_window = + IsStackedBelow(window1_, window2_) ? window2_ : window1_; + aura::Window* target_window = top_window == window1_ ? window2_ : window1_; + auto* parent_container = target_window->parent(); + parent_container->StackChildBelow(target_window, top_window); + + if (ShouldConsiderDivider()) { + // Update the stacking order of the `split_view_divider` to be on top of the + // `top_window` which makes the overall stacking order become + // `divider_widget->GetNativeWindow()` --> `top_window` --> `target_window`. + aura::Window* root_window = window1_->GetRootWindow(); + auto* split_view_controller = SplitViewController::Get(root_window); + DCHECK(split_view_controller); + auto* split_view_divider = split_view_controller->split_view_divider(); + DCHECK(split_view_divider); + auto* divider_widget = split_view_divider->divider_widget(); + DCHECK(divider_widget); + parent_container->StackChildAbove(divider_widget->GetNativeWindow(), + top_window); + } +} + void SnapGroup::OnWindowDestroying(aura::Window* window) { if (window != window1_ && window != window2_) { return; @@ -40,37 +79,6 @@ Shell::Get()->snap_group_controller()->RemoveSnapGroup(this); } -void SnapGroup::OnWindowActivated(ActivationReason reason, - aura::Window* gained_active, - aura::Window* lost_active) { - if (gained_active != window1_ && gained_active != window2_) { - return; - } - - // Update the stacking order of the other window in the snap group so that the - // two windows are always placed on top, both of which will be stacked below - // the `split_view_divider` if applicable afterwards. - aura::Window* target_window = gained_active == window1_ ? window2_ : window1_; - auto* parent_container = target_window->parent(); - parent_container->StackChildBelow(target_window, gained_active); - - // Update the stacking order of the `split_view_divider` to be on top of the - // `gained_active` window which makes the overall stacking order become - // `divider_widget->GetNativeWindow()` --> `gained_active` --> - // `target_window`. - if (ShouldConsiderDivider()) { - aura::Window* root_window = window1_->GetRootWindow(); - auto* split_view_controller = SplitViewController::Get(root_window); - DCHECK(split_view_controller); - auto* split_view_divider = split_view_controller->split_view_divider(); - DCHECK(split_view_divider); - auto* divider_widget = split_view_divider->divider_widget(); - DCHECK(divider_widget); - parent_container->StackChildAbove(divider_widget->GetNativeWindow(), - gained_active); - } -} - void SnapGroup::StartObservingWindows(aura::Window* window1, aura::Window* window2) { DCHECK(window1); @@ -79,8 +87,6 @@ window1_->AddObserver(this); window2_ = window2; window2_->AddObserver(this); - - Shell::Get()->activation_client()->AddObserver(this); } void SnapGroup::StopObservingWindows() { @@ -93,8 +99,6 @@ window2_->RemoveObserver(this); window2_ = nullptr; } - - Shell::Get()->activation_client()->RemoveObserver(this); } } // namespace ash \ No newline at end of file
diff --git a/ash/wm/snap_group/snap_group.h b/ash/wm/snap_group/snap_group.h index 39fc19ff..bd4ae78 100644 --- a/ash/wm/snap_group/snap_group.h +++ b/ash/wm/snap_group/snap_group.h
@@ -6,7 +6,6 @@ #define ASH_WM_SNAP_GROUP_SNAP_GROUP_H_ #include "ui/aura/window_observer.h" -#include "ui/wm/public/activation_change_observer.h" namespace aura { class Window; @@ -16,7 +15,7 @@ // Observes changes in the windows of the SnapGroup and manages the windows // accordingly. -class SnapGroup : public aura::WindowObserver, wm::ActivationChangeObserver { +class SnapGroup : public aura::WindowObserver { public: SnapGroup(aura::Window* window1, aura::Window* window2); SnapGroup(const SnapGroup&) = delete; @@ -24,15 +23,10 @@ ~SnapGroup() override; // aura::WindowObserver: - // TODO: Implement `OnWindowParentChanged` and maybe `OnWindowStackingChanged` - // in future. + // TODO: Implement `OnWindowParentChanged`. + void OnWindowStackingChanged(aura::Window* window) override; void OnWindowDestroying(aura::Window* window) override; - // wm::ActivationChangeObserver: - void OnWindowActivated(ActivationReason reason, - aura::Window* gained_active, - aura::Window* lost_active) override; - aura::Window* window1() const { return window1_; } aura::Window* window2() const { return window2_; }
diff --git a/ash/wm/snap_group/snap_group_unittest.cc b/ash/wm/snap_group/snap_group_unittest.cc index 26e17d3f..1b77543 100644 --- a/ash/wm/snap_group/snap_group_unittest.cc +++ b/ash/wm/snap_group/snap_group_unittest.cc
@@ -34,6 +34,7 @@ #include "ui/base/l10n/l10n_util.h" #include "ui/gfx/geometry/point.h" #include "ui/gfx/geometry/rect.h" +#include "ui/gfx/geometry/vector2d.h" #include "ui/gfx/image/image_skia.h" #include "ui/gfx/image/image_unittest_util.h" #include "ui/gfx/paint_vector_icon.h" @@ -282,42 +283,18 @@ EXPECT_FALSE(split_view_controller()->InSplitViewMode()); } -// Tests that after snapping two windows, resize one window will end the -// split view mode. -TEST_F(SnapGroupEntryPointArm1Test, ResizeOneWindowWillExitSnapGroup) { - std::unique_ptr<aura::Window> w1(CreateTestWindow()); - std::unique_ptr<aura::Window> w2(CreateTestWindow()); - SnapTwoTestWindowsInArm1(w1.get(), w2.get()); - - auto* event_generator = GetEventGenerator(); - wm::ActivateWindow(w1.get()); - - gfx::Point hover_location = w1->GetBoundsInScreen().right_center(); - hover_location.Offset(-1, 0); - const int distance_delta = work_area_bounds().width() / 4; - event_generator->MoveMouseTo(hover_location); - event_generator->PressLeftButton(); - event_generator->MoveMouseTo(hover_location.x() + distance_delta, - hover_location.y()); - event_generator->ReleaseLeftButton(); - EXPECT_FALSE(split_view_controller()->InSplitViewMode()); - EXPECT_FALSE(split_view_divider()); -} - -// Tests the basic functionalities when dragging the windows in a snap group -// with the split view divider. -TEST_F(SnapGroupEntryPointArm1Test, - ResizeWithSplitViewDividerBasicFunctionalities) { +// Tests the snap ratio is updated correctly when resizing the windows in a snap +// group with the split view divider. +TEST_F(SnapGroupEntryPointArm1Test, SnapRatioTest) { std::unique_ptr<aura::Window> w1(CreateTestWindow()); std::unique_ptr<aura::Window> w2(CreateTestWindow()); SnapTwoTestWindowsInArm1(w1.get(), w2.get()); const gfx::Point hover_location = split_view_divider_bounds_in_screen().CenterPoint(); - const int distance_delta = -work_area_bounds().width() / 6; split_view_controller()->StartResizeWithDivider(hover_location); const auto end_point = - gfx::Point(hover_location.x() + distance_delta, hover_location.y()); + hover_location + gfx::Vector2d(-work_area_bounds().width() / 6, 0); split_view_controller()->ResizeWithDivider(end_point); split_view_controller()->EndResizeWithDivider(end_point); EXPECT_TRUE(split_view_controller()->InSplitViewMode()); @@ -341,9 +318,8 @@ const gfx::Point hover_location = split_view_divider_bounds_in_screen().CenterPoint(); split_view_controller()->StartResizeWithDivider(hover_location); - const auto end_location = - gfx::Point(hover_location.x() + distance_delta, hover_location.y()); - split_view_controller()->ResizeWithDivider(end_location); + split_view_controller()->ResizeWithDivider( + hover_location + gfx::Vector2d(distance_delta, 0)); EXPECT_TRUE(split_view_controller()->InSplitViewMode()); EXPECT_EQ(w1_cached_bounds.width() + distance_delta, @@ -483,6 +459,29 @@ } } +// Tests that the hit area of the split view divider can be outside of its +// bounds with the extra insets with a value of `kSplitViewDividerExtraInset`. +TEST_F(SnapGroupEntryPointArm1Test, SplitViewDividerEnlargedHitArea) { + std::unique_ptr<aura::Window> w1(CreateTestWindow()); + std::unique_ptr<aura::Window> w2(CreateTestWindow()); + SnapTwoTestWindowsInArm1(w1.get(), w2.get(), /*horizontal=*/true); + + const gfx::Point cached_divider_center_point = + split_view_divider_bounds_in_screen().CenterPoint(); + auto* event_generator = GetEventGenerator(); + gfx::Point hover_location = + cached_divider_center_point - + gfx::Vector2d(kSplitViewDividerExtraInset.width(), 0); + event_generator->MoveMouseTo(hover_location); + event_generator->PressLeftButton(); + const auto move_vector = -gfx::Vector2d(20, 0); + event_generator->MoveMouseTo(hover_location + move_vector); + event_generator->ReleaseLeftButton(); + EXPECT_TRUE(split_view_controller()->InSplitViewMode()); + EXPECT_EQ(split_view_divider_bounds_in_screen().CenterPoint(), + cached_divider_center_point + move_vector); +} + // A test fixture that tests the user-initiated snap group entry point. This // entry point is guarded by the feature flag `kSnapGroup` and will only be // enabled when the feature param `kAutomaticallyLockGroup` is false.
diff --git a/ash/wm/splitview/split_view_constants.h b/ash/wm/splitview/split_view_constants.h index ea25e20..4206aa9 100644 --- a/ash/wm/splitview/split_view_constants.h +++ b/ash/wm/splitview/split_view_constants.h
@@ -8,6 +8,7 @@ #include "ash/ash_export.h" #include "base/time/time.h" #include "chromeos/ui/wm/constants.h" +#include "ui/gfx/geometry/insets.h" namespace ash { @@ -87,6 +88,10 @@ constexpr int kMinCaretKeyboardDist = 16; constexpr float kMinDividerPositionRatio = 0.15f; +// Extra insets used to increase the hit bounds of the split view divider to +// make it easier to handle located event. +constexpr gfx::Insets kSplitViewDividerExtraInset = gfx::Insets::VH(0, -2); + } // namespace ash #endif // ASH_WM_SPLITVIEW_SPLIT_VIEW_CONSTANTS_H_
diff --git a/ash/wm/splitview/split_view_divider.cc b/ash/wm/splitview/split_view_divider.cc index 23ce672..ddd974d 100644 --- a/ash/wm/splitview/split_view_divider.cc +++ b/ash/wm/splitview/split_view_divider.cc
@@ -64,7 +64,7 @@ } // namespace // ----------------------------------------------------------------------------- -// DividerView: +// AlwaysOnTopWindowTargeter: // The window targeter that is installed on the always on top container window // when the split view mode is active. @@ -313,8 +313,9 @@ for (auto snap_pos : {SplitViewController::SnapPosition::kPrimary, SplitViewController::SnapPosition::kSecondary}) { auto* window = controller_->GetSnappedWindow(snap_pos); - if (window) + if (window) { AddObservedWindow(window); + } } } @@ -554,7 +555,16 @@ divider_view_ = divider_widget_->SetContentsView( std::make_unique<DividerView>(controller, this)); divider_widget_->SetBounds(GetDividerBoundsInScreen(/*is_dragging=*/false)); - divider_widget_->GetNativeWindow()->SetProperty(kLockedToRootKey, true); + auto* divider_widget_native_window = divider_widget_->GetNativeWindow(); + divider_widget_native_window->SetProperty(kLockedToRootKey, true); + + // Use a window targeter and enlarge the hit region to allow located events + // that are slightly outside the divider widget bounds be consumed by + // `divider_widget_`. + auto window_targeter = std::make_unique<aura::WindowTargeter>(); + window_targeter->SetInsets(kSplitViewDividerExtraInset); + divider_widget_native_window->SetEventTargeter(std::move(window_targeter)); + divider_widget_->Show(); }
diff --git a/ash/wm/splitview/split_view_divider.h b/ash/wm/splitview/split_view_divider.h index 54b9209..3d4730d 100644 --- a/ash/wm/splitview/split_view_divider.h +++ b/ash/wm/splitview/split_view_divider.h
@@ -52,7 +52,7 @@ int divider_position, bool is_dragging); - views::Widget* divider_widget() { return divider_widget_; } + views::Widget* divider_widget() const { return divider_widget_; } // Do the divider spawning animation that adds a finishing touch to the // snapping animation of a window.
diff --git a/base/allocator/partition_allocator/address_pool_manager.h b/base/allocator/partition_allocator/address_pool_manager.h index 30bb9341..a2366cb7 100644 --- a/base/allocator/partition_allocator/address_pool_manager.h +++ b/base/allocator/partition_allocator/address_pool_manager.h
@@ -8,7 +8,6 @@ #include <bitset> #include <limits> -#include "base/allocator/partition_allocator/address_pool_manager_bitmap.h" #include "base/allocator/partition_allocator/address_pool_manager_types.h" #include "base/allocator/partition_allocator/partition_address_space.h" #include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h" @@ -21,6 +20,10 @@ #include "base/allocator/partition_allocator/partition_lock.h" #include "build/build_config.h" +#if !BUILDFLAG(HAS_64_BIT_POINTERS) +#include "base/allocator/partition_allocator/address_pool_manager_bitmap.h" +#endif + namespace partition_alloc { class AddressSpaceStatsDumper;
diff --git a/base/allocator/partition_allocator/partition_root.cc b/base/allocator/partition_allocator/partition_root.cc index 93a9b51e..ff2d108 100644 --- a/base/allocator/partition_allocator/partition_root.cc +++ b/base/allocator/partition_allocator/partition_root.cc
@@ -6,7 +6,6 @@ #include <cstdint> -#include "base/allocator/partition_allocator/address_pool_manager_bitmap.h" #include "base/allocator/partition_allocator/freeslot_bitmap.h" #include "base/allocator/partition_allocator/oom.h" #include "base/allocator/partition_allocator/page_allocator.h" @@ -38,6 +37,10 @@ #include "base/allocator/partition_allocator/starscan/pcscan.h" #endif +#if !BUILDFLAG(HAS_64_BIT_POINTERS) +#include "base/allocator/partition_allocator/address_pool_manager_bitmap.h" +#endif + #if BUILDFLAG(IS_WIN) #include <windows.h> #include "wow64apiset.h"
diff --git a/base/allocator/partition_allocator/pointers/raw_ptr_backup_ref_impl.h b/base/allocator/partition_allocator/pointers/raw_ptr_backup_ref_impl.h index ac7edc6..bb4ce014 100644 --- a/base/allocator/partition_allocator/pointers/raw_ptr_backup_ref_impl.h +++ b/base/allocator/partition_allocator/pointers/raw_ptr_backup_ref_impl.h
@@ -9,7 +9,6 @@ #include <type_traits> -#include "base/allocator/partition_allocator/address_pool_manager_bitmap.h" #include "base/allocator/partition_allocator/partition_address_space.h" #include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h" #include "base/allocator/partition_allocator/partition_alloc_base/component_export.h" @@ -20,6 +19,10 @@ #include "base/allocator/partition_allocator/partition_alloc_forward.h" #include "build/build_config.h" +#if !BUILDFLAG(HAS_64_BIT_POINTERS) +#include "base/allocator/partition_allocator/address_pool_manager_bitmap.h" +#endif + #if !BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) #error "Included under wrong build option" #endif
diff --git a/base/allocator/partition_allocator/starscan/pcscan_internal.cc b/base/allocator/partition_allocator/starscan/pcscan_internal.cc index 1e1d88d9..81df4405 100644 --- a/base/allocator/partition_allocator/starscan/pcscan_internal.cc +++ b/base/allocator/partition_allocator/starscan/pcscan_internal.cc
@@ -18,7 +18,6 @@ #include <vector> #include "base/allocator/partition_allocator/address_pool_manager.h" -#include "base/allocator/partition_allocator/address_pool_manager_bitmap.h" #include "base/allocator/partition_allocator/allocation_guard.h" #include "base/allocator/partition_allocator/page_allocator.h" #include "base/allocator/partition_allocator/page_allocator_constants.h" @@ -52,6 +51,10 @@ #include "base/allocator/partition_allocator/thread_cache.h" #include "build/build_config.h" +#if !BUILDFLAG(HAS_64_BIT_POINTERS) +#include "base/allocator/partition_allocator/address_pool_manager_bitmap.h" +#endif + #if PA_CONFIG(STARSCAN_NOINLINE_SCAN_FUNCTIONS) #define PA_SCAN_INLINE PA_NOINLINE #else
diff --git a/base/android/javatests/OWNERS b/base/android/javatests/OWNERS new file mode 100644 index 0000000..7d712d23 --- /dev/null +++ b/base/android/javatests/OWNERS
@@ -0,0 +1 @@ +file://base/test/android/OWNERS
diff --git a/base/guid.cc b/base/guid.cc index 548a40d4..a65b3ce5e 100644 --- a/base/guid.cc +++ b/base/guid.cc
@@ -67,22 +67,10 @@ return !GetCanonicalGUIDInternal(input, /*strict=*/false).empty(); } -bool IsValidGUID(StringPiece16 input) { - return !GetCanonicalGUIDInternal(input, /*strict=*/false).empty(); -} - bool IsValidGUIDOutputString(StringPiece input) { return !GetCanonicalGUIDInternal(input, /*strict=*/true).empty(); } -std::string RandomDataToGUIDString(const uint64_t bytes[2]) { - return StringPrintf( - "%08x-%04x-%04x-%04x-%012llx", static_cast<uint32_t>(bytes[0] >> 32), - static_cast<uint32_t>((bytes[0] >> 16) & 0x0000ffff), - static_cast<uint32_t>(bytes[0] & 0x0000ffff), - static_cast<uint32_t>(bytes[1] >> 48), bytes[1] & 0x0000ffff'ffffffffULL); -} - // static GUID GUID::GenerateRandomV4() { uint8_t sixteen_bytes[kGuidV4InputLength]; @@ -125,7 +113,13 @@ sixteen_bytes[1] |= 0x80000000'00000000ULL; GUID guid; - guid.lowercase_ = RandomDataToGUIDString(sixteen_bytes); + guid.lowercase_ = + StringPrintf("%08x-%04x-%04x-%04x-%012llx", + static_cast<uint32_t>(sixteen_bytes[0] >> 32), + static_cast<uint32_t>((sixteen_bytes[0] >> 16) & 0x0000ffff), + static_cast<uint32_t>(sixteen_bytes[0] & 0x0000ffff), + static_cast<uint32_t>(sixteen_bytes[1] >> 48), + sixteen_bytes[1] & 0x0000ffff'ffffffffULL); return guid; }
diff --git a/base/guid.h b/base/guid.h index 0e0b367..061810d 100644 --- a/base/guid.h +++ b/base/guid.h
@@ -23,19 +23,17 @@ namespace base { -// DEPRECATED, use GUID::GenerateRandomV4() instead. +// DEPRECATED(crbug.com/1195446): Use GUID::GenerateRandomV4() instead. BASE_EXPORT std::string GenerateGUID(); -// DEPRECATED, use GUID::ParseCaseInsensitive() and GUID::is_valid() instead. +// DEPRECATED(crbug.com/1195446): Use GUID::ParseCaseInsensitive() and +// GUID::is_valid() instead. BASE_EXPORT bool IsValidGUID(StringPiece input); -BASE_EXPORT bool IsValidGUID(StringPiece16 input); -// DEPRECATED, use GUID::ParseLowercase() and GUID::is_valid() instead. +// DEPRECATED(crbug.com/1195446): Use GUID::ParseLowercase() and +// GUID::is_valid() instead. BASE_EXPORT bool IsValidGUIDOutputString(StringPiece input); -// For unit testing purposes only. Do not use outside of tests. -BASE_EXPORT std::string RandomDataToGUIDString(const uint64_t bytes[2]); - class BASE_EXPORT GUID { public: // Length in bytes of the input required to format the input as a GUID in the
diff --git a/base/guid_unittest.cc b/base/guid_unittest.cc index bf0ff60..e83cf9a3 100644 --- a/base/guid_unittest.cc +++ b/base/guid_unittest.cc
@@ -16,19 +16,6 @@ namespace base { -TEST(GUIDTest, GUIDGeneratesAllZeroes) { - static constexpr uint64_t kBytes[] = {0, 0}; - const std::string clientid = RandomDataToGUIDString(kBytes); - EXPECT_EQ("00000000-0000-0000-0000-000000000000", clientid); -} - -TEST(GUIDTest, GUIDGeneratesCorrectly) { - static constexpr uint64_t kBytes[] = {0x0123456789ABCDEFULL, - 0xFEDCBA9876543210ULL}; - const std::string clientid = RandomDataToGUIDString(kBytes); - EXPECT_EQ("01234567-89ab-cdef-fedc-ba9876543210", clientid); -} - TEST(GUIDTest, DeprecatedGUIDCorrectlyFormatted) { constexpr int kIterations = 10; for (int i = 0; i < kIterations; ++i) { @@ -128,20 +115,17 @@ } } -TEST(GUIDTest, Equality) { - static constexpr uint64_t kBytes[] = {0xDEADBEEFDEADBEEFULL, - 0xDEADBEEFDEADBEEFULL}; - const std::string clientid = RandomDataToGUIDString(kBytes); +TEST(GUIDTest, EqualityAndRoundTrip) { + static constexpr char kCanonicalStr[] = + "deadbeef-dead-4eef-bead-beefdeadbeef"; - static constexpr char kExpectedCanonicalStr[] = - "deadbeef-dead-beef-dead-beefdeadbeef"; - ASSERT_EQ(kExpectedCanonicalStr, clientid); + const GUID from_lower = + GUID::ParseCaseInsensitive(ToLowerASCII(kCanonicalStr)); + EXPECT_EQ(kCanonicalStr, from_lower.AsLowercaseString()); - const GUID from_lower = GUID::ParseCaseInsensitive(ToLowerASCII(clientid)); - EXPECT_EQ(kExpectedCanonicalStr, from_lower.AsLowercaseString()); - - const GUID from_upper = GUID::ParseCaseInsensitive(ToUpperASCII(clientid)); - EXPECT_EQ(kExpectedCanonicalStr, from_upper.AsLowercaseString()); + const GUID from_upper = + GUID::ParseCaseInsensitive(ToUpperASCII(kCanonicalStr)); + EXPECT_EQ(kCanonicalStr, from_upper.AsLowercaseString()); EXPECT_EQ(from_lower, from_upper);
diff --git a/build/config/rust.gni b/build/config/rust.gni index 68949af..e50ed8e9 100644 --- a/build/config/rust.gni +++ b/build/config/rust.gni
@@ -55,6 +55,10 @@ # <home dir>/.rustup/toolchains/nightly-<something>-<something> rust_sysroot_absolute = "" + # If you're using an external Rust toolchain, set this to a Rust + # the output of rustc -V. + rustc_version = "" + # If you're using a Rust toolchain as specified by rust_sysroot_absolute, # you can specify whether it supports nacl here. rust_toolchain_supports_nacl = false @@ -136,12 +140,14 @@ # (so e.g. rust targets are rebuilt, and the standard library is re-copied when # the toolchain changes). It is left empty for custom toolchains. rustc_revision = "" -if (toolchain_has_rust && rust_sysroot_absolute == "") { +if (toolchain_has_rust) { if (use_chromium_rust_toolchain) { update_rust_args = [ "--print-package-version" ] rustc_revision = exec_script("//tools/rust/update_rust.py", update_rust_args, "trim string") + } else if (rust_sysroot_absolute != "") { + rustc_revision = rustc_version } else { # Android toolchain version. rustc_revision = "rustc 1.64.0-dev (Android Rust Toolchain version 9099361)"
diff --git a/chrome/VERSION b/chrome/VERSION index d9b20b75..b2291c7 100644 --- a/chrome/VERSION +++ b/chrome/VERSION
@@ -1,4 +1,4 @@ MAJOR=114 MINOR=0 -BUILD=5679 +BUILD=5680 PATCH=0
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni index c730308..93b94db 100644 --- a/chrome/android/chrome_java_sources.gni +++ b/chrome/android/chrome_java_sources.gni
@@ -357,7 +357,6 @@ "java/src/org/chromium/chrome/browser/compositor/layouts/eventfilter/OverlayPanelEventFilter.java", "java/src/org/chromium/chrome/browser/compositor/layouts/phone/SimpleAnimationLayout.java", "java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/StackScroller.java", - "java/src/org/chromium/chrome/browser/compositor/overlays/strip/CascadingStripStacker.java", "java/src/org/chromium/chrome/browser/compositor/overlays/strip/ScrollingStripStacker.java", "java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java", "java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManager.java",
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiFeatureUtilities.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiFeatureUtilities.java index e9a2160..d469334 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiFeatureUtilities.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiFeatureUtilities.java
@@ -67,12 +67,6 @@ new BooleanCachedFieldTrialParameter(ChromeFeatureList.TAB_GRID_LAYOUT_ANDROID, SHOW_OPEN_IN_TAB_GROUP_MENU_ITEM_FIRST_PARAM, false); - // Field trial parameter for defining tab width for tab strip improvements. - private static final String TAB_STRIP_IMPROVEMENTS_TAB_WIDTH_PARAM = "min_tab_width"; - public static final DoubleCachedFieldTrialParameter TAB_STRIP_TAB_WIDTH = - new DoubleCachedFieldTrialParameter(ChromeFeatureList.TAB_STRIP_IMPROVEMENTS, - TAB_STRIP_IMPROVEMENTS_TAB_WIDTH_PARAM, 108.f); - // Field trial parameter for controlling share tabs in TabSelectionEditorV2. private static final String TAB_SELECTION_EDITOR_V2_SHARE_PARAM = "enable_share"; public static final BooleanCachedFieldTrialParameter ENABLE_TAB_SELECTION_EDITOR_V2_SHARE = @@ -148,21 +142,11 @@ } /** - * @return Whether the tab strip improvements are enabled. - * @param context The activity context. - */ - public static boolean isTabStripImprovementsEnabled(Context context) { - return DeviceFormFactor.isNonMultiDisplayContextOnTablet(context) - && ChromeFeatureList.sTabStripImprovements.isEnabled(); - } - - /** * @return Whether tab groups are enabled for tablet. * @param context The activity context. */ public static boolean isTabletTabGroupsEnabled(Context context) { return DeviceFormFactor.isNonMultiDisplayContextOnTablet(context) - && ChromeFeatureList.sTabStripImprovements.isEnabled() && ChromeFeatureList.sTabGroupsForTablets.isEnabled() && !DeviceClassManager.enableAccessibilityLayout(context); } @@ -226,25 +210,7 @@ && !SysUtils.isLowEndDevice(); } - private static Float sTabMinWidthForTesting; - - /** - * Set the min tab width for testing. - */ - public static void setTabMinWidthForTesting(@Nullable Float minWidth) { - sTabMinWidthForTesting = minWidth; - } - - /** - * @return The min tab width. - */ - public static float getTabMinWidth() { - if (sTabMinWidthForTesting != null) { - return sTabMinWidthForTesting; - } - - return (float) TAB_STRIP_TAB_WIDTH.getValue(); - } + public static Float sTabMinWidthForTesting; /** * @return Whether the "Open in new tab in group" context menu item should show before the
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogTest.java index 92e72fe..3371a25 100644 --- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogTest.java +++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogTest.java
@@ -40,7 +40,6 @@ import static org.chromium.chrome.browser.flags.ChromeFeatureList.TAB_GRID_LAYOUT_ANDROID; import static org.chromium.chrome.browser.flags.ChromeFeatureList.TAB_GROUPS_ANDROID; import static org.chromium.chrome.browser.flags.ChromeFeatureList.TAB_GROUPS_FOR_TABLETS; -import static org.chromium.chrome.browser.flags.ChromeFeatureList.TAB_STRIP_IMPROVEMENTS; import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.clickFirstCardFromTabSwitcher; import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.clickFirstTabInDialog; import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.clickNthTabInDialog; @@ -143,7 +142,7 @@ @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE}) @Restriction({Restriction.RESTRICTION_TYPE_NON_LOW_END_DEVICE}) @Features.EnableFeatures({TAB_GRID_LAYOUT_ANDROID, TAB_GROUPS_ANDROID, - TAB_GROUPS_FOR_TABLETS, TAB_STRIP_IMPROVEMENTS}) + TAB_GROUPS_FOR_TABLETS}) @DoNotBatch(reason = "crbug.com/1380489") public class TabGridDialogTest { // clang-format on
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorMenuTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorMenuTest.java index 0d18d1f9..8a0c4fe5e 100644 --- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorMenuTest.java +++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorMenuTest.java
@@ -18,7 +18,6 @@ import static org.chromium.chrome.browser.flags.ChromeFeatureList.TAB_GROUPS_FOR_TABLETS; import static org.chromium.chrome.browser.flags.ChromeFeatureList.TAB_SELECTION_EDITOR_V2; -import static org.chromium.chrome.browser.flags.ChromeFeatureList.TAB_STRIP_IMPROVEMENTS; import static org.chromium.ui.test.util.ViewUtils.onViewWaiting; import android.content.Context; @@ -82,7 +81,7 @@ */ @RunWith(ParameterizedRunner.class) @ParameterAnnotations.UseRunnerDelegate(ChromeJUnit4RunnerDelegate.class) -@Features.EnableFeatures({TAB_STRIP_IMPROVEMENTS, TAB_GROUPS_FOR_TABLETS, TAB_SELECTION_EDITOR_V2}) +@Features.EnableFeatures({TAB_GROUPS_FOR_TABLETS, TAB_SELECTION_EDITOR_V2}) @Batch(Batch.PER_CLASS) public class TabSelectionEditorMenuTest extends BlankUiTestActivityTestCase { private static final int TAB_COUNT = 3;
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorTest.java index 7a2a0dde..102f3823 100644 --- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorTest.java +++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorTest.java
@@ -31,7 +31,6 @@ import static org.chromium.base.test.util.Restriction.RESTRICTION_TYPE_NON_LOW_END_DEVICE; import static org.chromium.chrome.browser.flags.ChromeFeatureList.TAB_GROUPS_ANDROID; import static org.chromium.chrome.browser.flags.ChromeFeatureList.TAB_GROUPS_FOR_TABLETS; -import static org.chromium.chrome.browser.flags.ChromeFeatureList.TAB_STRIP_IMPROVEMENTS; import static org.chromium.chrome.browser.flags.ChromeFeatureList.TAB_TO_GTS_ANIMATION; import android.content.Intent; @@ -113,7 +112,8 @@ @RunWith(ChromeJUnit4ClassRunner.class) @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE, "force-fieldtrials=Study/Group", "force-fieldtrial-params=Study.Group:enable_launch_polish/true"}) -@EnableFeatures({TAB_GROUPS_ANDROID, TAB_STRIP_IMPROVEMENTS, TAB_GROUPS_FOR_TABLETS}) +@EnableFeatures({TAB_GROUPS_ANDROID + "<Study", + TAB_GROUPS_FOR_TABLETS}) @DisableFeatures(TAB_TO_GTS_ANIMATION) @Batch(Batch.PER_CLASS) public class TabSelectionEditorTest {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/UndoRefocusHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/UndoRefocusHelper.java index 93129c0..fa8eff7 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/UndoRefocusHelper.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/UndoRefocusHelper.java
@@ -12,7 +12,6 @@ import org.chromium.base.metrics.RecordUserAction; import org.chromium.base.supplier.ObservableSupplier; import org.chromium.chrome.browser.compositor.layouts.LayoutManagerImpl; -import org.chromium.chrome.browser.flags.ChromeFeatureList; import org.chromium.chrome.browser.layouts.LayoutStateProvider; import org.chromium.chrome.browser.layouts.LayoutType; import org.chromium.chrome.browser.lifecycle.DestroyObserver; @@ -56,8 +55,7 @@ public static void initialize(Context context, TabModelSelector modelSelector, ObservableSupplier<LayoutManagerImpl> layoutManagerObservableSupplier, boolean isTablet) { - if (!DeviceFormFactor.isNonMultiDisplayContextOnTablet(context) - || !ChromeFeatureList.sTabStripImprovements.isEnabled()) { + if (!DeviceFormFactor.isNonMultiDisplayContextOnTablet(context)) { return; }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/flags/ChromeCachedFlags.java b/chrome/android/java/src/org/chromium/chrome/browser/app/flags/ChromeCachedFlags.java index eede55e6..a38eee3 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/app/flags/ChromeCachedFlags.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/app/flags/ChromeCachedFlags.java
@@ -121,7 +121,6 @@ ChromeFeatureList.sTabGroupsContinuationAndroid, ChromeFeatureList.sTabGroupsForTablets, ChromeFeatureList.sTabSelectionEditorV2, - ChromeFeatureList.sTabStripImprovements, ChromeFeatureList.sTabToGTSAnimation, ChromeFeatureList.sToolbarUseHardwareBitmapDraw, ChromeFeatureList.sUseChimeAndroidSdk, @@ -170,7 +169,6 @@ TabUiFeatureUtilities.ZOOMING_MIN_MEMORY, TabUiFeatureUtilities.SKIP_SLOW_ZOOMING, TabUiFeatureUtilities.THUMBNAIL_ASPECT_RATIO, - TabUiFeatureUtilities.TAB_STRIP_TAB_WIDTH, TabUiFeatureUtilities.ENABLE_TAB_SELECTION_EDITOR_V2_LONGPRESS_ENTRY, TabUiFeatureUtilities.ENABLE_TAB_SELECTION_EDITOR_V2_SHARE, TabUiFeatureUtilities.ENABLE_TAB_SELECTION_EDITOR_V2_BOOKMARKS,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/CascadingStripStacker.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/CascadingStripStacker.java deleted file mode 100644 index 2a46758..0000000 --- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/CascadingStripStacker.java +++ /dev/null
@@ -1,131 +0,0 @@ -// Copyright 2016 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.chrome.browser.compositor.overlays.strip; - -import org.chromium.base.MathUtils; -import org.chromium.ui.base.LocalizationUtils; - -/** - * A stacker that tells the {@link StripLayoutHelper} how to layer the tabs for the - * {@link StaticLayout} when the available window width is >= 600dp. Tabs will be stacked with the - * focused tab in front with tabs cascading back to each side. - */ -public class CascadingStripStacker extends StripStacker { - @Override - public void setTabOffsets(int selectedIndex, StripLayoutTab[] indexOrderedTabs, - float tabStackWidth, int maxTabsToStack, float tabOverlapWidth, float stripLeftMargin, - float stripRightMargin, float stripWidth, boolean inReorderMode, boolean tabClosing, - boolean tabCreating, float cachedTabWidth) { - // 1. Calculate the size of the selected tab. This is used later to figure out how - // occluded the tabs are. - final StripLayoutTab selTab = selectedIndex >= 0 ? indexOrderedTabs[selectedIndex] : null; - final float selTabWidth = selTab != null ? selTab.getWidth() : 0; - final float selTabVisibleSize = selTabWidth - tabStackWidth - tabOverlapWidth; - - for (int i = 0; i < indexOrderedTabs.length; i++) { - StripLayoutTab tab = indexOrderedTabs[i]; - - float posX = tab.getIdealX(); - - // 2. Calculate how many tabs are stacked on the left or the right, giving us an idea - // of where we can stack this current tab. - int leftStackCount = (i < selectedIndex) ? Math.min(i, maxTabsToStack) - : Math.min(maxTabsToStack, selectedIndex) - + Math.min(maxTabsToStack, i - selectedIndex); - - int rightStackCount = (i >= selectedIndex) - ? Math.min(indexOrderedTabs.length - 1 - i, maxTabsToStack) - : Math.min(indexOrderedTabs.length - 1 - selectedIndex, maxTabsToStack) - + Math.min(selectedIndex - i, maxTabsToStack); - - if (LocalizationUtils.isLayoutRtl()) { - int oldLeft = leftStackCount; - leftStackCount = rightStackCount; - rightStackCount = oldLeft; - } - - // 3. Calculate the proper draw position for the tab. Clamp based on stacking - // rules. - float minDrawX = tabStackWidth * leftStackCount + stripLeftMargin; - float maxDrawX = stripWidth - tabStackWidth * rightStackCount - stripRightMargin; - - float drawX = - MathUtils.clamp(posX + tab.getOffsetX(), minDrawX, maxDrawX - tab.getWidth()); - - // TODO(dtrainor): Don't set drawX if the tab is closing? - tab.setDrawX(drawX); - tab.setDrawY(tab.getOffsetY()); - - // 4. Calculate how visible this tab is. - float visiblePercentage = 1.f; - if (i != selectedIndex) { - final float effectiveTabWidth = Math.max(tab.getWidth(), 1.f); - final boolean leftStack = - LocalizationUtils.isLayoutRtl() ? i > selectedIndex : i < selectedIndex; - final float minVisible = !leftStack ? minDrawX + selTabVisibleSize : minDrawX; - final float maxVisible = leftStack ? maxDrawX - selTabVisibleSize : maxDrawX; - - final float clippedTabWidth = - Math.min(posX + effectiveTabWidth, maxVisible) - Math.max(posX, minVisible); - visiblePercentage = MathUtils.clamp(clippedTabWidth / effectiveTabWidth, 0.f, 1.f); - } - tab.setVisiblePercentage(visiblePercentage); - - // 5. Calculate which index we start sliding content for. - // When reordering, we don't want to slide the content of the adjacent tabs. - int contentOffsetIndex = inReorderMode ? selectedIndex + 1 : selectedIndex; - - // 6. Calculate how much the tab is overlapped on the left side or right for RTL. - float hiddenAmount = 0.f; - if (i > contentOffsetIndex && i > 0) { - // 6.a. Get the effective right edge of the previous tab. - final StripLayoutTab prevTab = indexOrderedTabs[i - 1]; - final float prevLayoutWidth = - (prevTab.getWidth() - tabOverlapWidth) * prevTab.getWidthWeight(); - float prevTabRight = prevTab.getDrawX(); - if (!LocalizationUtils.isLayoutRtl()) prevTabRight += prevLayoutWidth; - - // 6.b. Subtract our current draw X from the previous tab's right edge and - // get the percentage covered. - hiddenAmount = Math.max(prevTabRight - drawX, 0); - if (LocalizationUtils.isLayoutRtl()) { - // Invert The amount because we're RTL. - hiddenAmount = prevLayoutWidth - hiddenAmount; - } - } - - tab.setContentOffsetX(hiddenAmount); - } - } - - @Override - public void performOcclusionPass(int selectedIndex, StripLayoutTab[] indexOrderedTabs, - float stripWidth) { - for (int i = 1; i < indexOrderedTabs.length; i++) { - StripLayoutTab prevTab = indexOrderedTabs[i - 1]; - StripLayoutTab currTab = indexOrderedTabs[i]; - - if ((int) prevTab.getDrawY() == (int) currTab.getDrawY() - && (int) prevTab.getDrawX() == (int) currTab.getDrawX()) { - if (i <= selectedIndex) { - prevTab.setVisible(false); - } else if (i > selectedIndex) { - currTab.setVisible(false); - } - } else if ((int) prevTab.getDrawX() != (int) currTab.getDrawX()) { - if (i <= selectedIndex) { - prevTab.setVisible(true); - } else if (i > selectedIndex) { - currTab.setVisible(true); - } - } - - if (i == selectedIndex) currTab.setVisible(true); - - // If index 0 is selected, this line is required to set its visibility correctly. - if (i - 1 == selectedIndex) prevTab.setVisible(true); - } - } -} \ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/ScrollingStripStacker.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/ScrollingStripStacker.java index d4a1ed271..8a599fd 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/ScrollingStripStacker.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/ScrollingStripStacker.java
@@ -4,9 +4,6 @@ package org.chromium.chrome.browser.compositor.overlays.strip; -import org.chromium.chrome.browser.flags.ChromeFeatureList; -import org.chromium.ui.base.LocalizationUtils; - /** * A stacker that tells the {@link StripLayoutHelper} how to layer the tabs for the * {@link StaticLayout} when the available window width is < 600dp. Tabs will be stacked side by @@ -18,18 +15,13 @@ float tabStackWidth, int maxTabsToStack, float tabOverlapWidth, float stripLeftMargin, float stripRightMargin, float stripWidth, boolean inReorderMode, boolean tabClosing, boolean tabCreating, float cachedTabWidth) { - boolean tabStripImpEnabled = ChromeFeatureList.sTabStripImprovements.isEnabled(); for (int i = 0; i < indexOrderedTabs.length; i++) { StripLayoutTab tab = indexOrderedTabs[i]; - if (tabStripImpEnabled) { - // When a tab is closed, drawX and width update will be animated so skip this. - if (!tabClosing) { - tab.setDrawX(tab.getIdealX() + tab.getOffsetX()); - // When a tab is being created, all tabs are animating to their desired width. - if (!tabCreating) tab.setWidth(cachedTabWidth); - } - } else { - tab.setDrawX(tab.getIdealX()); + // When a tab is closed, drawX and width update will be animated so skip this. + if (!tabClosing) { + tab.setDrawX(tab.getIdealX() + tab.getOffsetX()); + // When a tab is being created, all tabs are animating to their desired width. + if (!tabCreating) tab.setWidth(cachedTabWidth); } tab.setDrawY(tab.getOffsetY()); tab.setVisiblePercentage(1.f); @@ -38,78 +30,11 @@ } @Override - public float computeNewTabButtonOffset(StripLayoutTab[] indexOrderedTabs, float tabOverlapWidth, - float stripLeftMargin, float stripRightMargin, float stripWidth, float buttonWidth, - float touchTargetOffset, float cachedTabWidth, boolean animate) { - if (ChromeFeatureList.sTabStripImprovements.isEnabled()) { - return super.computeNewTabButtonOffset(indexOrderedTabs, tabOverlapWidth, - stripLeftMargin, stripRightMargin, stripWidth, buttonWidth, touchTargetOffset, - cachedTabWidth, animate); - } - return LocalizationUtils.isLayoutRtl() - ? computeNewTabButtonOffsetRtl(indexOrderedTabs, tabOverlapWidth, stripRightMargin, - stripWidth, buttonWidth) - : computeNewTabButtonOffsetLtr(indexOrderedTabs, tabOverlapWidth, stripLeftMargin); - } - - @Override - public void performOcclusionPass(int selectedIndex, StripLayoutTab[] indexOrderedTabs, - float stripWidth) { + public void performOcclusionPass( + int selectedIndex, StripLayoutTab[] indexOrderedTabs, float stripWidth) { for (int i = 0; i < indexOrderedTabs.length; i++) { StripLayoutTab tab = indexOrderedTabs[i]; tab.setVisible((tab.getDrawX() + tab.getWidth()) >= 0 && tab.getDrawX() <= stripWidth); } } - - private float computeNewTabButtonOffsetLtr( - StripLayoutTab[] indexOrderedTabs, float tabOverlapWidth, float stripLeftMargin) { - float rightEdge = stripLeftMargin; - - int numTabs = indexOrderedTabs.length; - // Need to look at the last two tabs to determine the new tab position in case the last - // tab is dying. - int i = numTabs > 0 ? ((numTabs >= 2) ? numTabs - 2 : numTabs - 1) : 0; - for (; i < numTabs; i++) { - StripLayoutTab tab = indexOrderedTabs[i]; - float layoutWidth; - if (tab.isDying()) { - // If a tab is dying, adjust the tab width by the width weight so that the new - // tab button slides to the left with the closing tab. - layoutWidth = tab.getWidth() * tab.getWidthWeight(); - } else { - // If a tab is being created, disregard its width weight so the new tab button - // doesn't end up positioned too far to the left. If a tab is neither being - // created or dying, its width width weight is 1.0 and can also be ignored. - layoutWidth = tab.getWidth(); - } - - rightEdge = Math.max(tab.getDrawX() + layoutWidth, rightEdge); - } - - // Adjust the right edge by the tab overlap width so that the new tab button is nestled - // closer to the tab. - rightEdge -= tabOverlapWidth / 2; - - // The draw X position for the new tab button is the rightEdge of the tab strip. - return rightEdge; - } - - private float computeNewTabButtonOffsetRtl(StripLayoutTab[] indexOrderedTabs, - float tabOverlapWidth, float stripRightMargin, float stripWidth, - float newTabButtonWidth) { - float leftEdge = stripWidth - stripRightMargin; - int numTabs = indexOrderedTabs.length; - if (numTabs >= 1) { - StripLayoutTab tab = indexOrderedTabs[numTabs - 1]; - leftEdge = Math.min(tab.getDrawX(), leftEdge); - } - - // Adjust the left edge by the tab overlap width so that the new tab button is nestled - // closer to the tab. - leftEdge += tabOverlapWidth / 2; - - // The draw X position for the new tab button is the left edge of the tab strip minus - // the new tab button width. - return leftEdge - newTabButtonWidth; - } } \ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java index 2cd2162..62aeba8 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java
@@ -58,7 +58,6 @@ import org.chromium.components.browser_ui.styles.ChromeColors; import org.chromium.components.browser_ui.styles.SemanticColorUtils; import org.chromium.components.browser_ui.widget.animation.Interpolators; -import org.chromium.ui.base.DeviceFormFactor; import org.chromium.ui.base.LocalizationUtils; import org.chromium.ui.util.ColorUtils; @@ -120,6 +119,8 @@ private static final int ANIM_TAB_DIM_MS = 150; private static final int ANIM_BUTTONS_FADE_MS = 150; private static final int NEW_TAB_BUTTON_OFFSET_MOVE_MS = 250; + private static final int SCROLL_DISTANCE_SHORT = 960; + private static final int SCROLL_DISTANCE_MEDIUM = 1920; private static final long INVALID_TIME = 0L; static final long DROP_INTO_GROUP_MS = 300L; @@ -156,6 +157,7 @@ static final float BACKGROUND_TAB_BRIGHTNESS_DEFAULT = 1.f; static final float BACKGROUND_TAB_BRIGHTNESS_DIMMED = 0.65f; static final float FADE_FULL_OPACITY_THRESHOLD_DP = 24.f; + private static final float TAB_STRIP_TAB_WIDTH = 108.f; private static final int MESSAGE_RESIZE = 1; private static final int MESSAGE_UPDATE_SPINNER = 2; @@ -171,9 +173,7 @@ private TabModel mModel; private TabGroupModelFilter mTabGroupModelFilter; private TabCreator mTabCreator; - private StripStacker mStripStacker; - private CascadingStripStacker mCascadingStripStacker = new CascadingStripStacker(); - private ScrollingStripStacker mScrollingStripStacker = new ScrollingStripStacker(); + private StripStacker mStripStacker = new ScrollingStripStacker(); // Internal State private StripLayoutTab[] mStripTabs = new StripLayoutTab[0]; @@ -230,8 +230,6 @@ private float mNewTabButtonWithTabStripEndPadding; private final boolean mIncognito; - // Whether the CascadingStripStacker should be used. - private boolean mShouldCascadeTabs; private boolean mIsFirstLayoutPass; private boolean mAnimationsDisabledForTesting; // Whether tab strip scrolling is in progress @@ -247,9 +245,6 @@ private boolean mTabGroupMarginAnimRunning; private boolean mTabCreating; - // Experiment flags - private final boolean mTabStripImpEnabled; - /** * Creates an instance of the {@link StripLayoutHelper}. * @param context The current Android {@link Context}. @@ -274,7 +269,6 @@ mNewTabButtonWidth = NEW_TAB_BUTTON_WIDTH_DP; } mModelSelectorButton = modelSelectorButton; - mTabStripImpEnabled = ChromeFeatureList.sTabStripImprovements.isEnabled(); if (ChromeFeatureList.sTabStripRedesign.isEnabled()) { mNewTabButtonWithTabStripEndPadding = @@ -290,7 +284,7 @@ ? mNewTabButtonWithTabStripEndPadding + mNewTabButtonWidth : 0; - mMinTabWidth = TabUiFeatureUtilities.getTabMinWidth(); + mMinTabWidth = TAB_STRIP_TAB_WIDTH; mMaxTabWidth = TabUiThemeUtil.getMaxTabStripTabWidthDp(); mReorderMoveStartThreshold = REORDER_MOVE_START_THRESHOLD_DP; @@ -416,10 +410,6 @@ int menuWidth = mContext.getResources().getDimensionPixelSize(R.dimen.menu_width); mTabMenu.setWidth(menuWidth); mTabMenu.setModal(true); - - mShouldCascadeTabs = - DeviceFormFactor.isNonMultiDisplayContextOnTablet(context) && !mTabStripImpEnabled; - mStripStacker = mShouldCascadeTabs ? mCascadingStripStacker : mScrollingStripStacker; mIsFirstLayoutPass = true; } @@ -499,8 +489,6 @@ * @return The opacity to use for the fade. */ private float getFadeOpacity(boolean isLeft) { - if (mShouldCascadeTabs) return 0.f; - // In RTL, scroll position 0 is on the right side of the screen, whereas in LTR scroll // position 0 is on the left. Account for that in the offset calculation. boolean isRtl = LocalizationUtils.isLayoutRtl(); @@ -569,9 +557,8 @@ public void onSizeChanged(float width, float height, boolean orientationChanged, long time) { if (mWidth == width && mHeight == height) return; - boolean wasSelectedTabVisible = mTabStripImpEnabled && orientationChanged && mModel != null - && mModel.index() >= 0 && mModel.index() < mStripTabs.length - && mStripTabs[mModel.index()].isVisible(); + boolean wasSelectedTabVisible = orientationChanged && mModel != null && mModel.index() >= 0 + && mModel.index() < mStripTabs.length && mStripTabs[mModel.index()].isVisible(); boolean widthChanged = mWidth != width; @@ -584,9 +571,6 @@ if (widthChanged) { computeAndUpdateTabWidth(false, false); - boolean shouldCascade = - width >= DeviceFormFactor.MINIMUM_TABLET_WIDTH_DP && !mTabStripImpEnabled; - setShouldCascadeTabs(shouldCascade); } if (mStripTabs.length > 0) mUpdateHost.requestUpdate(); @@ -597,33 +581,6 @@ } /** - * Should be called when the viewport width crosses the 600dp threshold. The - * {@link CascadingStripStacker} should be used at 600dp+, otherwise the - * {@link ScrollingStripStacker} should be used. - * @param shouldCascadeTabs Whether the {@link CascadingStripStacker} should be used. - */ - void setShouldCascadeTabs(boolean shouldCascadeTabs) { - if (mModel == null) return; - - if (shouldCascadeTabs != mShouldCascadeTabs) { - mShouldCascadeTabs = shouldCascadeTabs; - setTabStacker(shouldCascadeTabs ? mCascadingStripStacker : mScrollingStripStacker); - - // Scroll to make the selected tab visible and nearby tabs visible. - if (mModel.getTabAt(mModel.index()) != null) { - updateScrollOffsetLimits(); - StripLayoutTab tab = findTabById(mModel.getTabAt(mModel.index()).getId()); - float delta = calculateOffsetToMakeTabVisible(tab, true, true, true, true); - // During this resize, mMinScrollOffset will be changing, so the scroll effect - // cannot be properly animated. Jump to the new scroll offset instead. - mScrollOffset = mScrollOffset + delta; - } - - updateStrip(); - } - } - - /** * Updates all internal resources and dimensions. * @param context The current Android {@link Context}. */ @@ -652,7 +609,6 @@ if (mModel == model) return; mModel = model; mTabCreator = tabCreator; - setShouldCascadeTabs(mShouldCascadeTabs); computeAndUpdateTabOrders(false, false); } @@ -823,42 +779,27 @@ // 3. Figure out which tab needs to be visible. StripLayoutTab fastExpandTab = findTabById(prevId); - boolean allowLeftExpand = false; - boolean canExpandSelectedTab = false; - if (!selected) { - fastExpandTab = tab; - allowLeftExpand = true; - } - if (!mShouldCascadeTabs) { - int selIndex = mModel.index(); - if (mTabStripImpEnabled && !selected && selIndex >= 0 && selIndex < mStripTabs.length) { - // Prioritize focusing on selected tab over newly created unselected tabs. - fastExpandTab = mStripTabs[selIndex]; - } else { - fastExpandTab = tab; - } - allowLeftExpand = true; - canExpandSelectedTab = true; + int selIndex = mModel.index(); + if (!selected && selIndex >= 0 && selIndex < mStripTabs.length) { + // Prioritize focusing on selected tab over newly created unselected tabs. + fastExpandTab = mStripTabs[selIndex]; + } else { + fastExpandTab = tab; } + boolean allowLeftExpand = true; + boolean canExpandSelectedTab = true; // 4. Scroll the stack so that the fast expand tab is visible. Skip if tab was restored. - boolean skipAutoScroll = (mTabStripImpEnabled && closureCancelled) || onStartup; + boolean skipAutoScroll = closureCancelled || onStartup; if (fastExpandTab != null && !skipAutoScroll) { float delta = calculateOffsetToMakeTabVisible( fastExpandTab, canExpandSelectedTab, allowLeftExpand, true, selected); - if (!mShouldCascadeTabs) { - // If the ScrollingStripStacker is being used and the new tab button is visible, go - // directly to the new scroll offset rather than animating. Animating the scroll - // causes the new tab button to disappear for a frame. - boolean shouldAnimate = (!mNewTabButton.isVisible() || mTabStripImpEnabled) - && !mAnimationsDisabledForTesting; - setScrollForScrollingTabStacker(delta, shouldAnimate, time); - } else if (delta != 0.f) { - mScroller.startScroll( - Math.round(mScrollOffset), 0, (int) delta, 0, time, getExpandDuration()); - } + // If new tab button is visible, go directly to the new scroll offset rather than + // animating. Animating the scroll causes the new tab button to disappear for a frame. + boolean shouldAnimate = !mAnimationsDisabledForTesting; + setScrollForScrollingTabStacker(delta, shouldAnimate, time); } // 5. When restoring tabs through startup, ensure the selected tab is visible, as the newly @@ -873,24 +814,15 @@ * partially visible tabs at the edge of the tab strip when min tab width is set to >=156dp. */ private void updateCloseButtons() { - if (!mTabStripImpEnabled) { - return; - } - Tab selectedTab = mModel.getTabAt(mModel.index()); final int count = mModel.getCount(); if (selectedTab == null) return; for (int i = 0; i < count; i++) { final StripLayoutTab tab = mStripTabs[i]; - if (mMinTabWidth == TAB_WIDTH_MEDIUM) { - boolean canShowCloseButton = shouldShowCloseButton(tab, i); - mStripTabs[i].setCanShowCloseButton(canShowCloseButton, !mIsFirstLayoutPass); - } else if (mMinTabWidth == TAB_WIDTH_SMALL) { - boolean canShowCloseButton = tab.getWidth() >= TAB_WIDTH_MEDIUM - || (tab.getId() == selectedTab.getId() && shouldShowCloseButton(tab, i)); - mStripTabs[i].setCanShowCloseButton(canShowCloseButton, !mIsFirstLayoutPass); - } + boolean canShowCloseButton = tab.getWidth() >= TAB_WIDTH_MEDIUM + || (tab.getId() == selectedTab.getId() && shouldShowCloseButton(tab, i)); + mStripTabs[i].setCanShowCloseButton(canShowCloseButton, !mIsFirstLayoutPass); } } @@ -1084,42 +1016,13 @@ // 2.b. Still scrolling, update the scroll destination here. mScroller.setFinalX((int) (mScroller.getFinalX() + deltaX)); } else { - // 2.c. Not scrolling. Check if we need to fast expand. - float fastExpandDelta; - if (mShouldCascadeTabs) { - fastExpandDelta = - calculateOffsetToMakeTabVisible(mInteractingTab, true, true, true, true); - } else { - // Non-cascaded tabs are never hidden behind each other, so there's no need to fast - // expand. - fastExpandDelta = 0.f; + // 2.c. Not scrolling. + if (!mIsStripScrollInProgress) { + mIsStripScrollInProgress = true; + RecordUserAction.record("MobileToolbarSlideTabs"); + onStripScrollStart(); } - - if (mInteractingTab != null && fastExpandDelta != 0.f) { - if ((fastExpandDelta > 0 && deltaX > 0) || (fastExpandDelta < 0 && deltaX < 0)) { - mScroller.startScroll(Math.round(mScrollOffset), 0, (int) fastExpandDelta, 0, - time, getExpandDuration()); - } - } else { - if (!mIsStripScrollInProgress) { - mIsStripScrollInProgress = true; - RecordUserAction.record("MobileToolbarSlideTabs"); - onStripScrollStart(); - } - updateScrollOffsetPosition(mScrollOffset + deltaX); - } - } - - // 3. Check if we should start the reorder mode. Disabling this for tab strip improvements - // experiments to prevent accidentally entering reorder mode when scrolling the tab strip. - if (!ChromeFeatureList.sTabStripImprovements.isEnabled() && !mInReorderMode) { - final float absTotalX = Math.abs(totalX); - final float absTotalY = Math.abs(totalY); - if (totalY > mReorderMoveStartThreshold && absTotalX < mReorderMoveStartThreshold * 2.f - && (absTotalX > EPSILON - && (absTotalY / absTotalX) > TAN_OF_REORDER_ANGLE_START_THRESHOLD)) { - startReorderMode(time, x, x - totalX); - } + updateScrollOffsetPosition(mScrollOffset + deltaX); } // If we're scrolling at all we aren't interacting with any particular tab. @@ -1257,10 +1160,11 @@ // Find out if we're closing the last tab to determine if we resize immediately. boolean lastTab = mStripTabs.length == 0 || mStripTabs[mStripTabs.length - 1].getId() == tab.getId(); - // For the improved tab strip design, when a tab is closed #resizeStripOnTabClose will run - // animations for the new tab offset and tab x offsets. When there is only 1 tab remaining, - // we do not need to run those animations, so #resizeTabStrip() is used instead. - boolean runImprovedTabAnimations = mTabStripImpEnabled && mStripTabs.length > 1; + + // When a tab is closed #resizeStripOnTabClose will run animations for the new tab offset + // and tab x offsets. When there is only 1 tab remaining, we do not need to run those + // animations, so #resizeTabStrip() is used instead. + boolean runImprovedTabAnimations = mStripTabs.length > 1; // 1. Setup the close animation. CompositorAnimator tabClosingAnimator = CompositorAnimator.ofFloatProperty( @@ -1505,27 +1409,19 @@ } } - private void updateScrollOffsetLimits() { + @VisibleForTesting + void updateScrollOffsetLimits() { // 1. Compute the width of the available space for all tabs. float stripWidth = mWidth - mLeftMargin - mRightMargin; // 2. Compute the effective width of every tab. - float tabsWidth = 0.f; - if (mShouldCascadeTabs) { + float tabsWidth = mStripTabs.length * (mCachedTabWidth - mTabOverlapWidth); + + if (mInReorderMode || mTabGroupMarginAnimRunning) { + tabsWidth += mStripStartMarginForReorder; for (int i = 0; i < mStripTabs.length; i++) { final StripLayoutTab tab = mStripTabs[i]; - tabsWidth += (tab.getWidth() - mTabOverlapWidth) * tab.getWidthWeight(); - } - } else { - // When tabs aren't cascaded, they're non-animating width weight is always 1.0 so it - // doesn't need to be included in this calculation. - tabsWidth = mStripTabs.length * (mCachedTabWidth - mTabOverlapWidth); - if (mInReorderMode || mTabGroupMarginAnimRunning) { - tabsWidth += mStripStartMarginForReorder; - for (int i = 0; i < mStripTabs.length; i++) { - final StripLayoutTab tab = mStripTabs[i]; - tabsWidth += tab.getTrailingMargin(); - } + tabsWidth += tab.getTrailingMargin(); } } @@ -1605,8 +1501,7 @@ private void pushStackerPropertiesToTab(StripLayoutTab tab) { // The close button is visible by default. If it should be hidden on tab creation, do not // animate the fade-out. See (https://crbug.com/1342654). - boolean shouldShowCloseButton = - (!mTabStripImpEnabled) || mCachedTabWidth >= TAB_WIDTH_MEDIUM; + boolean shouldShowCloseButton = mCachedTabWidth >= TAB_WIDTH_MEDIUM; tab.setCanShowCloseButton( mStripStacker.canShowCloseButton() && shouldShowCloseButton, false); // TODO(dtrainor): Push more properties as they are added (title text slide, etc?) @@ -1856,8 +1751,8 @@ if (selIndex >= 0 && selIndex < mStripTabs.length) { currentlyFocusedTab = mStripTabs[selIndex]; } - if (mTabStripImpEnabled && currentlyFocusedTab != null - && isSelectedTabCompletelyVisible(currentlyFocusedTab) && !selected) { + if (currentlyFocusedTab != null && isSelectedTabCompletelyVisible(currentlyFocusedTab) + && !selected) { // We want to prioritize keeping the currently selected tab in visible area if newly // created tab is not selected. If selected tab is already visible, no need to scroll. return 0.f; @@ -1871,30 +1766,14 @@ // 4. Return the proper deltaX that has to be applied to the current scroll to see the // tab. - if (mShouldCascadeTabs) { - if (mScrollOffset < optimalLeft && canExpandLeft) { - return optimalLeft - mScrollOffset; - } else if (mScrollOffset > optimalRight && canExpandRight) { - return optimalRight - mScrollOffset; - } - } else { - if (mTabStripImpEnabled) { - // Distance to make tab visible on the left edge and the right edge. - float offsetToOptimalLeft = optimalLeft - mScrollOffset; - float offsetToOptimalRight = optimalRight - mScrollOffset - mTabOverlapWidth; - // Scroll the minimum possible distance to bring the selected tab into visible area. - if (Math.abs(offsetToOptimalLeft) < Math.abs(offsetToOptimalRight)) { - return offsetToOptimalLeft; - } - return offsetToOptimalRight; - } - // If tabs are not cascaded, the entire tab strip scrolls and the strip should be - // scrolled to the optimal left offset. - return optimalLeft - mScrollOffset; + // Distance to make tab visible on the left edge and the right edge. + float offsetToOptimalLeft = optimalLeft - mScrollOffset; + float offsetToOptimalRight = optimalRight - mScrollOffset - mTabOverlapWidth; + // Scroll the minimum possible distance to bring the selected tab into visible area. + if (Math.abs(offsetToOptimalLeft) < Math.abs(offsetToOptimalRight)) { + return offsetToOptimalLeft; } - - // 5. We don't have to do anything. Return no delta. - return 0.f; + return offsetToOptimalRight; } @VisibleForTesting @@ -2029,14 +1908,9 @@ } setCompositorButtonsVisible(false); - // 6. Fast expand to make sure this tab is visible. If tabs are not cascaded, the selected - // tab will already be visible, so there's no need to fast expand to make it visible. - if (mShouldCascadeTabs) { - float fastExpandDelta = - calculateOffsetToMakeTabVisible(mInteractingTab, true, true, true, true); - mScroller.startScroll(Math.round(mScrollOffset), 0, (int) fastExpandDelta, 0, time, - getExpandDuration()); - } else if (TabUiFeatureUtilities.isTabletTabGroupsEnabled(mContext)) { + // 6. the selected tab will already be visible, so update tab group and background + // container. + if (TabUiFeatureUtilities.isTabletTabGroupsEnabled(mContext)) { Tab tab = getTabById(mInteractingTab.getId()); computeAndUpdateTabGroupMargins(true, animationList); if (ChromeFeatureList.sTabStripRedesign.isEnabled()) { @@ -2801,27 +2675,20 @@ * @return the duration in ms. */ private int getExpandDuration() { - if (mTabStripImpEnabled) { - float scrollDistance = Math.abs(mScrollOffset); - if (scrollDistance <= 960) { - return EXPAND_DURATION_MS; - } else if (scrollDistance <= 1920) { - return EXPAND_DURATION_MS_MEDIUM; - } else { - return EXPAND_DURATION_MS_LONG; - } + float scrollDistance = Math.abs(mScrollOffset); + if (scrollDistance <= SCROLL_DISTANCE_SHORT) { + return EXPAND_DURATION_MS; + } else if (scrollDistance <= SCROLL_DISTANCE_MEDIUM) { + return EXPAND_DURATION_MS_MEDIUM; + } else { + return EXPAND_DURATION_MS_LONG; } - - return EXPAND_DURATION_MS; } /** * Scrolls to the selected tab if it's not fully visible. */ private void bringSelectedTabToVisibleArea(long time, boolean animate) { - // The selected tab is always visible in the CascadingStripStacker. - if (mShouldCascadeTabs) return; - Tab selectedTab = mModel.getTabAt(mModel.index()); if (selectedTab == null) return; @@ -2833,20 +2700,15 @@ } private boolean isSelectedTabCompletelyVisible(StripLayoutTab selectedTab) { - if (mTabStripImpEnabled) { - boolean isRtl = LocalizationUtils.isLayoutRtl(); - if (isRtl) { - return selectedTab.isVisible() - && selectedTab.getDrawX() >= getCloseBtnVisibilityThreshold(false) - && selectedTab.getDrawX() + selectedTab.getWidth() <= mWidth; - } else { - return selectedTab.isVisible() && selectedTab.getDrawX() >= 0 - && selectedTab.getDrawX() + selectedTab.getWidth() <= getScrollableWidth(); - } + boolean isRtl = LocalizationUtils.isLayoutRtl(); + if (isRtl) { + return selectedTab.isVisible() + && selectedTab.getDrawX() >= getCloseBtnVisibilityThreshold(false) + && selectedTab.getDrawX() + selectedTab.getWidth() <= mWidth; + } else { + return selectedTab.isVisible() && selectedTab.getDrawX() >= 0 + && selectedTab.getDrawX() + selectedTab.getWidth() <= getScrollableWidth(); } - - return selectedTab.isVisible() && selectedTab.getDrawX() >= 0 - && selectedTab.getDrawX() + selectedTab.getWidth() <= mWidth; } /** @@ -2855,13 +2717,8 @@ * @return the width of the tab strip that is scrollable. */ private float getScrollableWidth() { - if (mTabStripImpEnabled) { - return mWidth - getCloseBtnVisibilityThreshold(false) - - (LocalizationUtils.isLayoutRtl() ? mRightMargin : mLeftMargin); - } - - // TODO(dtrainor): Use real tab widths here? - return mWidth - mLeftMargin - mRightMargin; + return mWidth - getCloseBtnVisibilityThreshold(false) + - (LocalizationUtils.isLayoutRtl() ? mRightMargin : mLeftMargin); } /** @@ -2873,6 +2730,8 @@ */ private float getCloseBtnVisibilityThreshold(boolean start) { if (start) { + //@TODO(zheliooo) Add unit tests to cover start tab cases for testTabSelected in + // StripLayoutHelperTest // The start of the tab strip does not have the new tab and model selector button // so the threshold is constant. return CLOSE_BTN_VISIBILITY_THRESHOLD_START; @@ -2900,14 +2759,6 @@ mTabMenu.performItemClick(menuItemId); } - /** - * @return Whether the {@link CascadingStripStacker} is being used. - */ - @VisibleForTesting - boolean shouldCascadeTabs() { - return mShouldCascadeTabs; - } - @VisibleForTesting int getExpandDurationForTesting() { return getExpandDuration();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutTab.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutTab.java index a388daa..3df441d 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutTab.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutTab.java
@@ -36,9 +36,6 @@ import org.chromium.components.browser_ui.styles.ChromeColors; import org.chromium.components.browser_ui.styles.SemanticColorUtils; import org.chromium.ui.base.LocalizationUtils; -import org.chromium.ui.resources.AndroidResourceType; -import org.chromium.ui.resources.LayoutResource; -import org.chromium.ui.resources.ResourceManager; import org.chromium.ui.util.ColorUtils; import java.util.List; @@ -181,8 +178,7 @@ // Close Button Constants // Close button padding value comes from the built-in padding in the source png. private static final int CLOSE_BUTTON_PADDING_DP = 7; - private static final int CLOSE_BUTTON_WIDTH_DP = 36; - private static final int CLOSE_BUTTON_WIDTH_SCROLLING_STRIP_DP = 48; + private static final int CLOSE_BUTTON_WIDTH_DP = 48; // Tab strip content y offset private static final float FOLIO_CONTENT_OFFSET_Y = 8.f; @@ -876,9 +872,7 @@ } private RectF getCloseRect() { - boolean tabStripImprovementsEnabled = ChromeFeatureList.sTabStripImprovements.isEnabled(); - int closeButtonWidth = tabStripImprovementsEnabled ? CLOSE_BUTTON_WIDTH_SCROLLING_STRIP_DP - : CLOSE_BUTTON_WIDTH_DP; + int closeButtonWidth = CLOSE_BUTTON_WIDTH_DP; if (!LocalizationUtils.isLayoutRtl()) { mClosePlacement.left = getWidth() - closeButtonWidth; mClosePlacement.right = mClosePlacement.left + closeButtonWidth; @@ -890,21 +884,7 @@ mClosePlacement.top = 0; mClosePlacement.bottom = getHeight(); - float xOffset = 0; - if (!tabStripImprovementsEnabled) { - ResourceManager manager = mRenderHost.getResourceManager(); - if (manager != null) { - LayoutResource resource = - manager.getResource(AndroidResourceType.STATIC, getResourceId()); - if (resource != null) { - xOffset = LocalizationUtils.isLayoutRtl() - ? resource.getPadding().left - : -(resource.getBitmapSize().width() - resource.getPadding().right); - } - } - } - - mClosePlacement.offset(getDrawX() + xOffset, getDrawY()); + mClosePlacement.offset(getDrawX(), getDrawY()); return mClosePlacement; }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripStacker.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripStacker.java index 19a09c7b5..4458a32 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripStacker.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripStacker.java
@@ -6,7 +6,6 @@ import org.chromium.base.MathUtils; import org.chromium.chrome.browser.compositor.layouts.Layout; -import org.chromium.chrome.browser.flags.ChromeFeatureList; import org.chromium.ui.base.LocalizationUtils; /** @@ -97,13 +96,11 @@ float tabOverlapWidth, float stripLeftMargin, float stripRightMargin, float stripWidth, float touchTargetOffset, float cachedTabWidth, boolean animate) { float rightEdge = stripLeftMargin; - boolean tabStripImpEnabled = ChromeFeatureList.sTabStripImprovements.isEnabled(); - for (StripLayoutTab tab : indexOrderedTabs) { float tabWidth; float tabDrawX; float tabWidthWeight; - if (tabStripImpEnabled && animate) { + if (animate) { // This value is set to 1.f to avoid the new tab button jitter for the improved tab // strip design. The tab.width and tab.drawX may not reflect the final values before // the tab closing animations are completed. @@ -132,12 +129,10 @@ float stripLeftMargin, float stripRightMargin, float stripWidth, float newTabButtonWidth, float touchTargetOffset, float cachedTabWidth, boolean animate) { - boolean tabStripImpEnabled = ChromeFeatureList.sTabStripImprovements.isEnabled(); - float leftEdge = stripWidth - stripRightMargin; for (StripLayoutTab tab : indexOrderedTabs) { - float drawX = (tabStripImpEnabled && animate) ? tab.getIdealX() : tab.getDrawX(); + float drawX = animate ? tab.getIdealX() : tab.getDrawX(); leftEdge = Math.min(drawX, leftEdge); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabActivity.java index 2b015ea8..ee43c71 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabActivity.java
@@ -487,8 +487,7 @@ // CustomTabActivityNavigationController#FinishHandler. Pass the mode enum into // CustomTabActivityModule, so that it can provide the correct implementation. getComponent().resolveTwaFinishHandler().onFinish(defaultBehavior); - } else if (intentDataProvider.isPartialCustomTab() - && intentDataProvider.shouldAnimateOnFinish()) { + } else if (intentDataProvider.isPartialCustomTab()) { // WebContents is missing during the close animation due to android:windowIsTranslucent. // We let partial CCT handle the animation. mBaseCustomTabRootUiCoordinator.handleCloseAnimation(defaultBehavior);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/RequestDesktopUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/RequestDesktopUtils.java index f5f5c159..309076b0 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/tab/RequestDesktopUtils.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/RequestDesktopUtils.java
@@ -396,15 +396,7 @@ return false; } - // If the smallest screen size in dp is below threshold, avoid default-enabling the setting. - if (context.getResources().getConfiguration().smallestScreenWidthDp - < ChromeFeatureList.getFieldTrialParamByFeatureAsInt(feature, - PARAM_GLOBAL_SETTING_DEFAULT_ON_SMALLEST_SCREEN_WIDTH, - DEFAULT_GLOBAL_SETTING_DEFAULT_ON_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP)) { - // TODO(shuyng): Add downgrade path support for smallestScreenWidthDp change. - return false; - } - + // Check whether manufacturer is in allow list. if (sDefaultEnabledManufacturerAllowlist == null) { sDefaultEnabledManufacturerAllowlist = new HashSet<>(); String allowListStr = ChromeFeatureList.getFieldTrialParamByFeature( @@ -416,9 +408,17 @@ if (!sDefaultEnabledManufacturerAllowlist.isEmpty() && !sDefaultEnabledManufacturerAllowlist.contains( Build.MANUFACTURER.toLowerCase(Locale.US))) { + updateNoLongerInCohort(); return false; } + if (displaySizeInInches > MAX_RECORDED_SCREEN_SIZE_INCHES) { + silentlyReportingCrashes( + context, displaySizeInInches, "Display size falls into overflow bucket"); + } + + // If it is not external display and the screen size in inches is below threshold, avoid + // default-enabling the setting. SharedPreferencesManager sharedPreferencesManager = SharedPreferencesManager.getInstance(); boolean isOnExternalDisplay = !ChromeFeatureList.getFieldTrialParamByFeatureAsBoolean( @@ -427,12 +427,23 @@ double screenSizeThreshold = ChromeFeatureList.getFieldTrialParamByFeatureAsDouble(feature, PARAM_GLOBAL_SETTING_DEFAULT_ON_DISPLAY_SIZE_THRESHOLD_INCHES, DEFAULT_GLOBAL_SETTING_DEFAULT_ON_DISPLAY_SIZE_THRESHOLD_INCHES); - if (displaySizeInInches < screenSizeThreshold && !isOnExternalDisplay - && sharedPreferencesManager.contains( + if (!isOnExternalDisplay && displaySizeInInches < screenSizeThreshold) { + if (sharedPreferencesManager.contains( ChromePreferenceKeys.DEFAULT_ENABLE_DESKTOP_SITE_GLOBAL_SETTING_COHORT)) { - // TODO(shuyng): Add downgrade path support for displaySizeInInches change. - silentlyReportingCrashes( - context, displaySizeInInches, "Display size falls below threshold"); + silentlyReportingCrashes( + context, displaySizeInInches, "Display size falls below threshold"); + } + updateNoLongerInCohort(); + return false; + } + + // If the smallest screen size in dp is below threshold, avoid default-enabling the setting. + if (context.getResources().getConfiguration().smallestScreenWidthDp + < ChromeFeatureList.getFieldTrialParamByFeatureAsInt(feature, + PARAM_GLOBAL_SETTING_DEFAULT_ON_SMALLEST_SCREEN_WIDTH, + DEFAULT_GLOBAL_SETTING_DEFAULT_ON_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP)) { + updateNoLongerInCohort(); + return false; } boolean previouslyDefaultEnabled = sharedPreferencesManager.readBoolean( @@ -441,8 +452,7 @@ SingleCategorySettingsConstants .USER_ENABLED_DESKTOP_SITE_GLOBAL_SETTING_PREFERENCE_KEY); - boolean inCohort = !previouslyUpdatedByUser && displaySizeInInches >= screenSizeThreshold - && !isOnExternalDisplay; + boolean inCohort = !previouslyUpdatedByUser && !isOnExternalDisplay; boolean wouldEnable = !previouslyDefaultEnabled && inCohort; if (wouldEnable) { // Store a SharedPreferences key to tag the device as qualified for the feature @@ -452,11 +462,6 @@ captureDisplaySpec(context, displaySizeInInches); } - if (displaySizeInInches > MAX_RECORDED_SCREEN_SIZE_INCHES) { - silentlyReportingCrashes( - context, displaySizeInInches, "Display size falls into overflow bucket"); - } - if (inCohort || sharedPreferencesManager.contains( ChromePreferenceKeys.DEFAULT_ENABLE_DESKTOP_SITE_GLOBAL_SETTING_COHORT)) {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/overlays/strip/TabStripTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/overlays/strip/TabStripTest.java index 21c62e9..38405f4 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/overlays/strip/TabStripTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/overlays/strip/TabStripTest.java
@@ -11,7 +11,6 @@ import org.hamcrest.Matchers; import org.junit.Assert; -import org.junit.Before; import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; @@ -60,11 +59,6 @@ public BlankCTATabInitialStateRule mBlankCTATabInitialStateRule = new BlankCTATabInitialStateRule(sActivityTestRule, false); - @Before - public void setUp() throws ExecutionException { - setShouldCascadeTabsAndCheckTabStrips(true); - } - /** * Tests that the initial state of the system is good. This is so the default TabStrips match * the TabModels and we do not have to test this in further tests. @@ -524,27 +518,23 @@ } /** - * Compares tab strips with models after switching between the ScrollingStripStacker and - * CascadingStripStacker when an incognito tab is present. Tests tapping the incognito - * button while the strip is using the ScrollingStripStacker (other tests cover tapping - * the button while using the CascadingStripStacker), and checks that switching between - * tab models scrolls to make the selected tab visible. + * Tests tapping the incognito button when an incognito tab is present and checks scrolls to + * make the selected tab visible. */ @Test @LargeTest @Restriction(UiRestriction.RESTRICTION_TYPE_TABLET) @Feature({"TabStrip"}) - public void testSwitchStripStackersWithIncognito() throws Exception { + public void testScrollingStripStackersWithIncognito() throws Exception { // Open an incognito tab to switch to the incognito model. sActivityTestRule.newIncognitoTabFromMenu(); - // Open enough regular tabs to cause the tabs to cascade or the strip to scroll depending - // on which stacker is being used. + // Open enough regular tabs to cause the tab strip to scroll. ChromeTabUtils.newTabsFromMenu( InstrumentationRegistry.getInstrumentation(), sActivityTestRule.getActivity(), 20); - // Switch to the ScrollingStripStacker. - setShouldCascadeTabsAndCheckTabStrips(false); + // Check scrolling tab strip + checkTabStrips(); // Scroll so the selected tab is not visible. assertSetTabStripScrollOffset(0); @@ -573,45 +563,35 @@ // Wait for selected tab to be visible. helper.waitForCallback(0); - - // Switch to the CascadingStripStacker. - setShouldCascadeTabsAndCheckTabStrips(true); } /** - * Compares tab strip with model after switching between the ScrollingStripStacker and - * CascadingStripStacker when the last tab is selected. This also verifies that the strip - * scrolls correctly and the correct index is selected after switching. + * This verifies that the strip scrolls correctly when last tab is selected. */ @Test @LargeTest @Restriction(UiRestriction.RESTRICTION_TYPE_TABLET) @Feature({"TabStrip"}) - public void testSwitchStripStackersWithLastTabSelected() throws Exception { - // Open enough regular tabs to cause the tabs to cascade or the strip to scroll depending - // on which stacker is being used. + public void testScrollingStripStackersWithLastTabSelected() throws Exception { + // Open enough regular tabs to cause the strip to scroll ChromeTabUtils.newTabsFromMenu( InstrumentationRegistry.getInstrumentation(), sActivityTestRule.getActivity(), 20); - // Switch to the ScrollingStripStacker. - setShouldCascadeTabsAndCheckTabStrips(false); - - // Switch to the CascadingStripStacker. - setShouldCascadeTabsAndCheckTabStrips(true); + // Check scrolling tab strip + checkTabStrips(); } /** - * Compares tab strip with model after switching between the ScrollingStripStacker and - * CascadingStripStacker when the first tab is selected. This also verifies that the strip - * scrolls correctly and the correct index is selected after switching. + * Verifies that the strip scrolls correctly and the correct index is selected. */ @Test @LargeTest @Restriction(UiRestriction.RESTRICTION_TYPE_TABLET) @Feature({"TabStrip"}) - public void testSwitchStripStackersWithFirstTabSelected() throws Exception { - // Open enough regular tabs to cause the tabs to cascade or the strip to scroll depending - // on which stacker is being used. + @DisabledTest + // Disable due to flakiness in scrolling to hide the first tab. + public void testScrollingStripStackersWithFirstTabSelected() throws Exception { + // Open enough regular tabs to cause the tab strip to scroll. ChromeTabUtils.newTabsFromMenu( InstrumentationRegistry.getInstrumentation(), sActivityTestRule.getActivity(), 20); @@ -619,38 +599,50 @@ // try to tap on it. ChromeTabUtils.switchTabInCurrentTabModel(sActivityTestRule.getActivity(), 0); - // Switch to the ScrollingStripStacker. - setShouldCascadeTabsAndCheckTabStrips(false); + // Check scrolling tab strip + checkTabStrips(); - // Scroll so the first tab is off screen to verify that switching to the - // CascadingStripStacker makes it visible again. The selected tab should always be visible - // when using the CascadingStripStacker but may not be visible when using the - // ScrollingStripStacker. - assertSetTabStripScrollOffset( - (int) TabStripUtils.getActiveStripLayoutHelper(sActivityTestRule.getActivity()) - .getMinimumScrollOffset()); StripLayoutTab selectedLayoutTab = TabStripUtils.findStripLayoutTab(sActivityTestRule.getActivity(), false, sActivityTestRule.getActivity().getCurrentTabModel().getTabAt(0).getId()); - assertTabVisibility(false, selectedLayoutTab); - // Switch to the CascadingStripStacker. - setShouldCascadeTabsAndCheckTabStrips(true); + // Create visibility callback helper. + final CallbackHelper helper = new CallbackHelper(); + TestThreadUtils.runOnUiThreadBlocking(() -> { + selectedLayoutTab.addObserver(new StripLayoutTab.Observer() { + @Override + public void onVisibilityChanged(boolean visible) { + // Notify the helper when tab becomes visible. + if (!visible) helper.notifyCalled(); + } + }); + }); + + // Flaky in scrolling to hide the first tab. + + // Scroll so the first tab is off screen and the selected tab may or may not be visible with + // the ScrollingStripStacker. + assertSetTabStripScrollOffset( + (int) TabStripUtils.getActiveStripLayoutHelper(sActivityTestRule.getActivity()) + .getMinimumScrollOffset()); + + // Tab should now be hidden. + helper.waitForCallback(0); + + assertTabVisibility(false, selectedLayoutTab); } /** - * Compares tab strip with model after switching between the ScrollingStripStacker and - * CascadingStripStacker when a middle tab is selected. This also verifies that the strip - * scrolls correctly and the correct index is selected after switching. + * Verifies that the strip scrolls correctly and the correct index when a middle tab is + * selected. */ @Test @LargeTest @Restriction(UiRestriction.RESTRICTION_TYPE_TABLET) @Feature({"TabStrip"}) @DisabledTest(message = "crbug.com/1348310") - public void testSwitchStripStackersWithMiddleTabSelected() throws Exception { - // Open enough regular tabs to cause the tabs to cascade or the strip to scroll depending - // on which stacker is being used. + public void testScrollingStripStackersWithMiddleTabSelected() throws Exception { + // Open enough regular tabs to cause the tab strip to scroll. ChromeTabUtils.newTabsFromMenu( InstrumentationRegistry.getInstrumentation(), sActivityTestRule.getActivity(), 10); @@ -658,11 +650,8 @@ // try to tap on it. ChromeTabUtils.switchTabInCurrentTabModel(sActivityTestRule.getActivity(), 5); - // Switch to the ScrollingStripStacker. - setShouldCascadeTabsAndCheckTabStrips(false); - - // Switch to the CascadingStripStacker. - setShouldCascadeTabsAndCheckTabStrips(true); + // Check scrolling tab strip + checkTabStrips(); } /** @@ -675,8 +664,8 @@ @Restriction(UiRestriction.RESTRICTION_TYPE_TABLET) @Feature({"TabStrip"}) public void testScrollingStripStackerFadeOpacity() throws Exception { - // Switch to the ScrollingStripStacker. - setShouldCascadeTabsAndCheckTabStrips(false); + // Check scrolling tab strip + checkTabStrips(); // Open enough regular tabs to cause the strip to scroll. ChromeTabUtils.newTabsFromMenu( @@ -719,8 +708,8 @@ @Restriction(UiRestriction.RESTRICTION_TYPE_TABLET) @Feature({"TabStrip"}) public void testScrollingStripStackerScrollsToSelectedTab() throws Exception { - // Switch to the ScrollingStripStacker. - setShouldCascadeTabsAndCheckTabStrips(false); + // Check scrolling tab strip + checkTabStrips(); // Open enough regular tabs to cause the strip to scroll. ChromeTabUtils.newTabsFromMenu( @@ -761,8 +750,8 @@ @Feature({"TabStrip"}) @DisabledTest(message = "crbug.com/1348310") public void testScrollingStripStackerTabOffsets() throws Exception { - // Switch to the ScrollingStripStacker. - setShouldCascadeTabsAndCheckTabStrips(false); + // Check scrolling tab strip + checkTabStrips(); // Open enough regular tabs to cause the strip to scroll and select the first tab. ChromeTabUtils.newTabsFromMenu( @@ -995,17 +984,9 @@ == incognito) { Assert.assertTrue("ChromeTab is not in the proper selection state", tabStrip.isForegroundTab(tabView)); - if (tabStrip.shouldCascadeTabs()) { - Assert.assertEquals( - "ChromeTab is not completely visible, but is selected. The selected " - + "tab should be visible when the CascadingStripStacker is in use.", - tabView.getVisiblePercentage(), 1.0f, 0); - } } - if (!tabStrip.shouldCascadeTabs()) { - assertTabVisibilityForScrollingStripStacker(tabStrip, tabView); - } + assertTabVisibilityForScrollingStripStacker(tabStrip, tabView); // TODO(dtrainor): Compare favicon bitmaps? Only compare a few pixels. } @@ -1061,39 +1042,27 @@ } /** - * Sets whether the strip should cascade tabs and checks for validity. - * - * @param shouldCascadeTabs Whether the {@link CascadingStripStacker} should be used. If false, - * the {@link ScrollingStripStacker} will be used instead. + * Check scrolling tab strip validity and auto-scrolling. + * @throws ExecutionException */ - private void setShouldCascadeTabsAndCheckTabStrips(final boolean shouldCascadeTabs) - throws ExecutionException { + private void checkTabStrips() throws ExecutionException { TabModel model = sActivityTestRule.getActivity().getCurrentTabModel(); int selectedTabIndex = model.index(); TestThreadUtils.runOnUiThreadBlocking(() -> { TabStripUtils.getStripLayoutHelper(sActivityTestRule.getActivity(), true) - .setShouldCascadeTabs(shouldCascadeTabs); + .updateScrollOffsetLimits(); TabStripUtils.getStripLayoutHelper(sActivityTestRule.getActivity(), false) - .setShouldCascadeTabs(shouldCascadeTabs); + .updateScrollOffsetLimits(); }); - // Assert that the correct StripStacker is being used. - Assert.assertEquals(shouldCascadeTabs - ? "Expected CascadingStripStacker but was ScrollingStripStacker." - : "Expected ScrollingStripStacker but was CascadingStripStacker.", - shouldCascadeTabs, - TabStripUtils.getActiveStripLayoutHelper(sActivityTestRule.getActivity()) - .shouldCascadeTabs()); - // Assert that the same tab is still selected. Assert.assertEquals("The correct tab is not selected.", selectedTabIndex, model.index()); // Compare all TabStrips with corresponding TabModels. compareAllTabStripsWithModel(); - // The selected tab should always be visible in the CascadingStripStacker and switching to - // the ScrollingStripStacker should auto-scroll to make the selected tab visible. + // The scrollingStripStacker should auto-scroll to make the selected tab visible. StripLayoutTab selectedLayoutTab = TabStripUtils.findStripLayoutTab(sActivityTestRule.getActivity(), model.isIncognito(), model.getTabAt(selectedTabIndex).getId());
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/media/PictureInPictureActivityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/media/PictureInPictureActivityTest.java index 9643877..0aca051b 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/media/PictureInPictureActivityTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/media/PictureInPictureActivityTest.java
@@ -54,6 +54,7 @@ import org.chromium.content_public.browser.test.util.TestThreadUtils; import org.chromium.content_public.browser.test.util.WebContentsUtils; import org.chromium.media_session.mojom.MediaSessionAction; +import org.chromium.ui.test.util.DeviceRestriction; import java.util.ArrayList; import java.util.concurrent.Callable; @@ -65,6 +66,7 @@ @RunWith(ChromeJUnit4ClassRunner.class) @Batch(Batch.PER_CLASS) @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE}) +@Restriction(DeviceRestriction.RESTRICTION_TYPE_NON_AUTO) @RequiresApi(Build.VERSION_CODES.O) public class PictureInPictureActivityTest { @Rule
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/ui/SyncErrorMessageTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/ui/SyncErrorMessageTest.java index 9600de1..ab86dcc 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/ui/SyncErrorMessageTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/ui/SyncErrorMessageTest.java
@@ -59,8 +59,7 @@ */ @RunWith(ChromeJUnit4ClassRunner.class) @DoNotBatch(reason = "TODO(crbug.com/1168590): SyncTestRule doesn't support batching.") -@EnableFeatures({ChromeFeatureList.MESSAGES_FOR_ANDROID_INFRASTRUCTURE, - ChromeFeatureList.MESSAGES_FOR_ANDROID_SYNC_ERROR}) +@EnableFeatures({ChromeFeatureList.MESSAGES_FOR_ANDROID_INFRASTRUCTURE}) @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE}) public class SyncErrorMessageTest { @Mock
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/ScrollingStripStackerUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/ScrollingStripStackerUnitTest.java index 5c13ac9..ec849db 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/ScrollingStripStackerUnitTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/ScrollingStripStackerUnitTest.java
@@ -4,8 +4,6 @@ package org.chromium.chrome.browser.compositor.overlays.strip; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; @@ -17,8 +15,6 @@ import org.mockito.MockitoAnnotations; import org.chromium.base.test.BaseRobolectricTestRunner; -import org.chromium.chrome.browser.flags.ChromeFeatureList; -import org.chromium.ui.base.LocalizationUtils; /** Tests for {@link ScrollingStripStacker}. */ @RunWith(BaseRobolectricTestRunner.class) @@ -59,27 +55,10 @@ draw_x += TAB_WIDTH; ideal_x += TAB_WIDTH; } - setTabStripImprovementFeature(false); } @Test - public void testSetTabOffsets() { - mTarget.setTabOffsets(2, mInput, 0, 0, 0, 0, 0, 0, false, false, false, CACHED_TAB_WIDTH); - - float expected_x = 0; - for (StripLayoutTab tab : mInput) { - verify(tab).setDrawX(expected_x); - verify(tab).setDrawY(TAB_OFFSET_Y); - verify(tab).setVisiblePercentage(1.f); - verify(tab).setContentOffsetX(0.f); - expected_x += TAB_WIDTH; - } - } - - @Test - public void testSetTabOffsets_withTabStripImprovement() { - setTabStripImprovementFeature(true); - + public void testSetTabOffsets_tabNotClosing() { mTarget.setTabOffsets(2, mInput, 0, 0, 0, 0, 0, 0, false, false, false, CACHED_TAB_WIDTH); float expected_x = 0; @@ -94,9 +73,7 @@ } @Test - public void testSetTabOffsets_withTabStripImprovement_tabClosing() { - setTabStripImprovementFeature(true); - + public void testSetTabOffsets_tabClosing() { mTarget.setTabOffsets( 2, mInput, 0, 0, 0, 0, 0, STRIP_WIDTH, false, true, false, CACHED_TAB_WIDTH); @@ -110,9 +87,7 @@ } @Test - public void testSetTabOffsets_withTabStripImprovement_tabCreating() { - setTabStripImprovementFeature(true); - + public void testSetTabOffsets_tabCreating() { mTarget.setTabOffsets( 2, mInput, 0, 0, 0, 0, 0, STRIP_WIDTH, false, false, true, CACHED_TAB_WIDTH); @@ -142,23 +117,4 @@ } } } - - @Test - public void testComputeNewTabButtonOffset() { - float value = mTarget.computeNewTabButtonOffset(mInput, TAB_OVERLAP, STRIP_MARGIN, - STRIP_MARGIN, STRIP_WIDTH, BUTTON_WIDTH, 0, CACHED_TAB_WIDTH, true); - assertThat("Button offset does not match", value, is(96.5f)); - } - - @Test - public void testComputeNewTabButtonOffsetRTL() { - LocalizationUtils.setRtlForTesting(true); - float value = mTarget.computeNewTabButtonOffset(mInput, TAB_OVERLAP, STRIP_MARGIN, - STRIP_MARGIN, STRIP_WIDTH, BUTTON_WIDTH, 0, CACHED_TAB_WIDTH, true); - assertThat("Button offset does not match", value, is(66.5f)); - } - - private void setTabStripImprovementFeature(boolean value) { - ChromeFeatureList.sTabStripImprovements.setForTesting(value); - } }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperTest.java index e99514f..8cf8c87 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperTest.java
@@ -57,7 +57,6 @@ import org.chromium.chrome.browser.layouts.components.VirtualView; import org.chromium.chrome.browser.tasks.tab_groups.TabGroupModelFilter; import org.chromium.chrome.browser.tasks.tab_management.TabManagementFieldTrial; -import org.chromium.chrome.browser.tasks.tab_management.TabUiFeatureUtilities; import org.chromium.chrome.test.util.browser.Features; import org.chromium.ui.base.LocalizationUtils; import org.chromium.ui.shadows.ShadowAppCompatResources; @@ -68,8 +67,9 @@ /** Tests for {@link StripLayoutHelper}. */ @RunWith(BaseRobolectricTestRunner.class) // clang-format off -@Features.EnableFeatures({ChromeFeatureList.TAB_STRIP_IMPROVEMENTS, - ChromeFeatureList.TAB_STRIP_REDESIGN, ChromeFeatureList.TAB_GROUPS_FOR_TABLETS}) +@Features.EnableFeatures({ + ChromeFeatureList.TAB_STRIP_REDESIGN, + ChromeFeatureList.TAB_GROUPS_FOR_TABLETS}) @Config(manifest = Config.NONE, qualifiers = "sw600dp", shadows = {ShadowAppCompatResources.class}) public class StripLayoutHelperTest { @@ -107,7 +107,7 @@ private static final float TAB_WIDTH_SMALL = 108.f; private static final float TAB_OVERLAP_WIDTH = 28.f; private static final float TAB_WIDTH_MEDIUM = 156.f; - private static final float TAB_MARGIN_WIDTH = 95.f; + private static final float TAB_MARGIN_WIDTH = 54.f; private static final long TIMESTAMP = 5000; private static final float NEW_TAB_BTN_X = 700.f; private static final float NEW_TAB_BTN_Y = 1400.f; @@ -130,7 +130,6 @@ mActivity = Robolectric.setupActivity(Activity.class); mActivity.setTheme(org.chromium.chrome.R.style.Theme_BrowserUI); - TabUiFeatureUtilities.setTabMinWidthForTesting(190.f); } @After @@ -139,8 +138,6 @@ mStripLayoutHelper.stopReorderModeForTesting(); mStripLayoutHelper.setTabAtPositionForTesting(null); } - - TabUiFeatureUtilities.setTabMinWidthForTesting(null); } /** @@ -151,7 +148,6 @@ @Test @Feature({"Accessibility"}) public void testSimpleTabOrder() { - TabUiFeatureUtilities.setTabMinWidthForTesting(TAB_WIDTH_MEDIUM); initializeTest(false, false, 0); mStripLayoutHelper.onSizeChanged(SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP); mStripLayoutHelper.updateLayout(TIMESTAMP); @@ -168,7 +164,6 @@ @Test @Feature({"Accessibility"}) public void testTabOrderWithIndex() { - TabUiFeatureUtilities.setTabMinWidthForTesting(TAB_WIDTH_SMALL); initializeTest(false, false, 1); mStripLayoutHelper.onSizeChanged(SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP); mStripLayoutHelper.updateLayout(TIMESTAMP); @@ -185,7 +180,6 @@ @Test @Feature({"Accessibility"}) public void testTabOrderRtl() { - TabUiFeatureUtilities.setTabMinWidthForTesting(TAB_WIDTH_SMALL); initializeTest(true, false, 0); mStripLayoutHelper.onSizeChanged(SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP); mStripLayoutHelper.updateLayout(TIMESTAMP); @@ -203,7 +197,6 @@ @Test @Feature({"Accessibility"}) public void testIncognitoAccessibilityDescriptions() { - TabUiFeatureUtilities.setTabMinWidthForTesting(TAB_WIDTH_SMALL); initializeTest(false, true, 0); mStripLayoutHelper.onSizeChanged(SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP); mStripLayoutHelper.updateLayout(TIMESTAMP); @@ -212,27 +205,6 @@ } @Test - @Feature("Tab Strip Improvements") - @Config(qualifiers = "sw800dp") - public void testStripStacker_TabStripImprovementsEnabled_Scroll() { - initializeTest(false, true, 0); - - // Assert - assertFalse(mStripLayoutHelper.shouldCascadeTabs()); - } - - @Test - @Feature("Tab Strip Improvements") - @Config(qualifiers = "sw800dp") - @Features.DisableFeatures(ChromeFeatureList.TAB_STRIP_IMPROVEMENTS) - public void testStripStacker_TabStripImprovementsDisabled_Cascade() { - initializeTest(false, true, 0); - - // Assert - assertTrue(mStripLayoutHelper.shouldCascadeTabs()); - } - - @Test public void testAllTabsClosed() { initializeTest(false, false, 0); assertTrue(mStripLayoutHelper.getStripLayoutTabs().length == TEST_TAB_TITLES.length); @@ -248,10 +220,8 @@ } @Test - @Feature("Tab Strip Improvements") - public void testStripStacker_UpdateCloseButtons() { - // Set fourth tab as selected - TabUiFeatureUtilities.setTabMinWidthForTesting(TAB_WIDTH_SMALL); + // Test show selected tab(non-last tab) close button. + public void testTabSelected_SelectedTab_NonLastTab_ShowCloseBtn() { initializeTest(false, true, 3); mStripLayoutHelper.onSizeChanged(SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP); StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_1); @@ -259,9 +229,9 @@ mStripLayoutHelper.tabSelected(1, 3, 0, false); - // Close btn should be visible on the selected tab. + // Close btn is visible on the selected tab. Mockito.verify(tabs[3]).setCanShowCloseButton(true, false); - // Close btn is hidden on unselected tabs. + // Close btn is hidden for the rest of tabs. Mockito.verify(tabs[0]).setCanShowCloseButton(false, false); Mockito.verify(tabs[1]).setCanShowCloseButton(false, false); Mockito.verify(tabs[2]).setCanShowCloseButton(false, false); @@ -269,10 +239,9 @@ } @Test - @Feature("Tab Strip Improvements") - public void testTabSelected_SelectedTab_EdgeTab_HideCloseBtn() { + // Test hide selected tab(non-last tab) close button. + public void testTabSelected_SelectedTab_NonLastTab_HideCloseBtn() { // Set fourth tab as selected - TabUiFeatureUtilities.setTabMinWidthForTesting(TAB_WIDTH_SMALL); initializeTest(false, true, 3); mStripLayoutHelper.onSizeChanged(SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP); StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_1); @@ -281,9 +250,9 @@ mStripLayoutHelper.tabSelected(1, 3, 0, false); - // Close btn should be hidden on the selected tab as its an edge tab. + // Close btn is hidden on the selected tab. Mockito.verify(tabs[3]).setCanShowCloseButton(false, false); - // Close btn is hidden on unselected tabs. + // Close btn is hidden for the rest of tabs as well. Mockito.verify(tabs[0]).setCanShowCloseButton(false, false); Mockito.verify(tabs[1]).setCanShowCloseButton(false, false); Mockito.verify(tabs[2]).setCanShowCloseButton(false, false); @@ -291,125 +260,66 @@ } @Test - @Feature("Tab Strip Improvements") - public void testTabSelected_EdgeTab_Start_Ltr_HideCloseBtn() { - // Arrange - TabUiFeatureUtilities.setTabMinWidthForTesting(TAB_WIDTH_MEDIUM); - initializeTest(false, true, 3); - StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_2); + // Test show selected tab(last tab) close button. + public void testTabSelected_SelectedTab_LastTab_ShowCloseBtn() { + initializeTest(false, true, 4); + StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_1); // Set mWidth value to 800.f mStripLayoutHelper.onSizeChanged(SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP); mStripLayoutHelper.getNewTabButton().setX(600.f); - // The leftmost tab is partially hidden - when(tabs[0].getDrawX()).thenReturn(-80.f); mStripLayoutHelper.setStripLayoutTabsForTest(tabs); // Act - mStripLayoutHelper.tabSelected(1, 3, 0, false); + mStripLayoutHelper.tabSelected(1, 4, 0, false); // Assert - // Close btn should be hidden for the partially visible edge tab. + // Close btn is visible on the selected last tab. + Mockito.verify(tabs[4]).setCanShowCloseButton(true, false); + // Close button is hidden for the rest of tabs. Mockito.verify(tabs[0]).setCanShowCloseButton(false, false); - // Close button is visible for the rest of the tabs. - Mockito.verify(tabs[1]).setCanShowCloseButton(true, false); - Mockito.verify(tabs[2]).setCanShowCloseButton(true, false); - Mockito.verify(tabs[3]).setCanShowCloseButton(true, false); - Mockito.verify(tabs[4]).setCanShowCloseButton(true, false); - } - - @Test - @Feature("Tab Strip Improvements") - public void testTabSelected_EdgeTab_End_Ltr_HideCloseBtn() { - // Arrange - TabUiFeatureUtilities.setTabMinWidthForTesting(TAB_WIDTH_MEDIUM); - initializeTest(false, false, 3); - StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_2); - // Set mWidth value to 800.f - mStripLayoutHelper.onSizeChanged(SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP); - mStripLayoutHelper.getNewTabButton().setX(700); - float drawXToHideCloseBtn = SCREEN_WIDTH - CLOSE_BTN_VISIBILITY_THRESHOLD_END_MODEL_SELECTOR - - TAB_WIDTH_2 + TAB_OVERLAP_WIDTH + 1; // 1 is to cross threshold - when(tabs[3].getDrawX()).thenReturn(drawXToHideCloseBtn); - mStripLayoutHelper.setStripLayoutTabsForTest(tabs); - - // Act - mStripLayoutHelper.tabSelected(1, 3, 0, false); - - // Assert - // Close button is visible for the rest of the tabs. - Mockito.verify(tabs[0]).setCanShowCloseButton(true, false); - Mockito.verify(tabs[1]).setCanShowCloseButton(true, false); - Mockito.verify(tabs[2]).setCanShowCloseButton(true, false); - Mockito.verify(tabs[4]).setCanShowCloseButton(true, false); - // Close btn should be hidden for the partially visible edge tab. + Mockito.verify(tabs[1]).setCanShowCloseButton(false, false); + Mockito.verify(tabs[2]).setCanShowCloseButton(false, false); Mockito.verify(tabs[3]).setCanShowCloseButton(false, false); } @Test - @Feature("Tab Strip Improvements") - public void testTabSelected_LastTab_ShowCloseBtn() { - TabUiFeatureUtilities.setTabMinWidthForTesting(TAB_WIDTH_MEDIUM); - initializeTest(false, false, 3); - StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_2); + // Test hide selected tab(last tab) close button. + public void testTabSelected_SelectedTab_LastTab_HideCloseBtn() { + initializeTest(false, true, 4); + StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_1); // Set mWidth value to 800.f mStripLayoutHelper.onSizeChanged(SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP); mStripLayoutHelper.getNewTabButton().setX(NEW_TAB_BTN_X); - // newTabBtn.X(700.f) - tab.width(160.f) + mTabOverlapWidth(24.f) - when(tabs[4].getDrawX()).thenReturn(564.f); + // mNewTabButton.getX(700.f) + TabOverlapWidth(28.f) - tab.getWidth(140) + 1 to cross + // threshold + when(tabs[4].getDrawX()).thenReturn(589.f); mStripLayoutHelper.setStripLayoutTabsForTest(tabs); // Act - mStripLayoutHelper.tabSelected(1, 3, 0, false); + mStripLayoutHelper.tabSelected(1, 4, 0, false); // Assert - // Close button is visible for all tabs. - Mockito.verify(tabs[0]).setCanShowCloseButton(true, false); - Mockito.verify(tabs[1]).setCanShowCloseButton(true, false); - Mockito.verify(tabs[2]).setCanShowCloseButton(true, false); - Mockito.verify(tabs[3]).setCanShowCloseButton(true, false); - Mockito.verify(tabs[4]).setCanShowCloseButton(true, false); - } - - @Test - @Feature("Tab Strip Improvements") - public void testTabSelected_LastTab_EdgeTab_HideCloseBtn() { - TabUiFeatureUtilities.setTabMinWidthForTesting(TAB_WIDTH_MEDIUM); - initializeTest(false, false, 3); - StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_2); - - // Set mWidth value to 800.f - mStripLayoutHelper.onSizeChanged(SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP); - mStripLayoutHelper.getNewTabButton().setX(NEW_TAB_BTN_X); - // newTabBtn.X(700.f) - tab.width(160.f) + mTabOverlapWidth(28.f) + 1 - when(tabs[4].getDrawX()).thenReturn(569.f); - mStripLayoutHelper.setStripLayoutTabsForTest(tabs); - - // Act - mStripLayoutHelper.tabSelected(1, 3, 0, false); - - // Assert - // Close btn should be visible for rest of the tabs. - Mockito.verify(tabs[0]).setCanShowCloseButton(true, false); - Mockito.verify(tabs[1]).setCanShowCloseButton(true, false); - Mockito.verify(tabs[2]).setCanShowCloseButton(true, false); - Mockito.verify(tabs[3]).setCanShowCloseButton(true, false); - // Close btn should be hidden for the partially visible edge tab. + // Close btn is hidden on the selected last tab. Mockito.verify(tabs[4]).setCanShowCloseButton(false, false); + // Close button is hidden for the rest of tabs. + Mockito.verify(tabs[0]).setCanShowCloseButton(false, false); + Mockito.verify(tabs[1]).setCanShowCloseButton(false, false); + Mockito.verify(tabs[2]).setCanShowCloseButton(false, false); + Mockito.verify(tabs[3]).setCanShowCloseButton(false, false); } @Test - @Feature("Tab Strip Improvements") - public void testTabSelected_EdgeTab_End_Ltr_NoModelSelBtn_HideCloseBtn() { + // Test hide selected tab(non-last tab) close button when there is no model selector button. + public void testTabSelected_NonLastTab_Ltr_NoModelSelBtn_HideCloseBtn() { // Arrange - TabUiFeatureUtilities.setTabMinWidthForTesting(TAB_WIDTH_MEDIUM); when(mModelSelectorBtn.isVisible()).thenReturn(false); initializeTest(false, false, 3); - StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_2); + StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_1); // Set mWidth value to 800.f mStripLayoutHelper.onSizeChanged(SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP); - mStripLayoutHelper.getNewTabButton().setX(700); - float drawXToHideCloseBtn = SCREEN_WIDTH - CLOSE_BTN_VISIBILITY_THRESHOLD_END - TAB_WIDTH_2 + mStripLayoutHelper.getNewTabButton().setX(NEW_TAB_BTN_X); + float drawXToHideCloseBtn = SCREEN_WIDTH - CLOSE_BTN_VISIBILITY_THRESHOLD_END - TAB_WIDTH_1 + TAB_OVERLAP_WIDTH + 1; // 1 is to cross threshold when(tabs[3].getDrawX()).thenReturn(drawXToHideCloseBtn); mStripLayoutHelper.setStripLayoutTabsForTest(tabs); @@ -418,65 +328,135 @@ mStripLayoutHelper.tabSelected(1, 3, 0, false); // Assert - // Close button is visible for the rest of the tabs. - Mockito.verify(tabs[0]).setCanShowCloseButton(true, false); - Mockito.verify(tabs[1]).setCanShowCloseButton(true, false); - Mockito.verify(tabs[2]).setCanShowCloseButton(true, false); - Mockito.verify(tabs[4]).setCanShowCloseButton(true, false); - // Close btn should be hidden for the partially visible edge tab. + // Close button is hidden for selected tab. + Mockito.verify(tabs[3]).setCanShowCloseButton(false, false); + // Close button is hidden for the rest of tabs as well. + Mockito.verify(tabs[0]).setCanShowCloseButton(false, false); + Mockito.verify(tabs[1]).setCanShowCloseButton(false, false); + Mockito.verify(tabs[2]).setCanShowCloseButton(false, false); + Mockito.verify(tabs[4]).setCanShowCloseButton(false, false); + } + + @Test + // Test show selected tab(non-last tab) close button when there is no model selector button. + public void testTabSelected_NonLastTab_Ltr_NoModelSelBtn_ShowCloseBtn() { + // Arrange + when(mModelSelectorBtn.isVisible()).thenReturn(false); + + initializeTest(false, false, 3); + StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_1); + // Set mWidth value to 800.f + mStripLayoutHelper.onSizeChanged(SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP); + mStripLayoutHelper.getNewTabButton().setX(NEW_TAB_BTN_X); + mStripLayoutHelper.setStripLayoutTabsForTest(tabs); + + // Act + mStripLayoutHelper.tabSelected(1, 3, 0, false); + + // Assert + // Close button is visible for selected tab + Mockito.verify(tabs[3]).setCanShowCloseButton(true, false); + // Close button is hidden for the rest of tabs. + Mockito.verify(tabs[0]).setCanShowCloseButton(false, false); + Mockito.verify(tabs[1]).setCanShowCloseButton(false, false); + Mockito.verify(tabs[2]).setCanShowCloseButton(false, false); + Mockito.verify(tabs[4]).setCanShowCloseButton(false, false); + } + + @Test + // Test hide selected tab(last tab) close button when RTL. + public void testTabSelected_SelectedTab_LastTab_Rtl_HideCloseBtn() { + initializeTest(true, false, 4); + StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_1); + // Set mWidth value to 800.f + mStripLayoutHelper.onSizeChanged(SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP); + mStripLayoutHelper.getNewTabButton().setX(NEW_TAB_BTN_X); + mStripLayoutHelper.setStripLayoutTabsForTest(tabs); + + // Act + mStripLayoutHelper.tabSelected(1, 4, 0, false); + + // Assert + // Close button is hidden for the selected last tab. + Mockito.verify(tabs[4]).setCanShowCloseButton(false, false); + // Close button is hidden for the rest of tabs as well. + Mockito.verify(tabs[0]).setCanShowCloseButton(false, false); + Mockito.verify(tabs[1]).setCanShowCloseButton(false, false); + Mockito.verify(tabs[2]).setCanShowCloseButton(false, false); Mockito.verify(tabs[3]).setCanShowCloseButton(false, false); } @Test - @Feature("Tab Strip Improvements") - public void testTabSelected_EdgeTab_Start_Rtl_HideCloseBtn() { - // Arrange - TabUiFeatureUtilities.setTabMinWidthForTesting(TAB_WIDTH_MEDIUM); - initializeTest(true, true, 3); - StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_2, 150.f, 5); - // Set mWidth value to 800.f. + // Test show selected tab(last tab) close button when RTL. + public void testTabSelected_SelectedTab_LastTab_Rtl_ShowCloseBtn() { + initializeTest(true, false, 4); + StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_1); + // Set mWidth value to 800.f mStripLayoutHelper.onSizeChanged(SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP); - // The leftmost tab is partially hidden. - when(tabs[0].getDrawX()).thenReturn(60.f); + mStripLayoutHelper.getNewTabButton().setX(600.f); + // newTabBtn.X(600.f) + newTabBtn.width(38.f) - mTabOverlapWidth(28.f) + 1 + when(tabs[4].getDrawX()).thenReturn(611.f); mStripLayoutHelper.setStripLayoutTabsForTest(tabs); // Act - mStripLayoutHelper.tabSelected(1, 3, 0, false); + mStripLayoutHelper.tabSelected(1, 4, 0, false); - // Assert - // Close btn should be hidden for the partially visible edge tab. - Mockito.verify(tabs[0]).setCanShowCloseButton(false, false); - // Close button is visible for the rest of the tabs. - Mockito.verify(tabs[1]).setCanShowCloseButton(true, false); - Mockito.verify(tabs[2]).setCanShowCloseButton(true, false); - Mockito.verify(tabs[3]).setCanShowCloseButton(true, false); + // Close button is visible for selected last tab. Mockito.verify(tabs[4]).setCanShowCloseButton(true, false); + // Assert + // Close button is hidden for the rest of tabs. + Mockito.verify(tabs[0]).setCanShowCloseButton(false, false); + Mockito.verify(tabs[1]).setCanShowCloseButton(false, false); + Mockito.verify(tabs[2]).setCanShowCloseButton(false, false); + Mockito.verify(tabs[3]).setCanShowCloseButton(false, false); } @Test - @Feature("Tab Strip Improvements") - public void testTabSelected_EdgeTab_End_Rtl_HideCloseBtn() { - // Arrange - TabUiFeatureUtilities.setTabMinWidthForTesting(TAB_WIDTH_MEDIUM); - initializeTest(true, true, 3); - StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_2, 150.f, 5); + // Test hide selected tab(non-last tab) close button when RTL. + public void testTabSelected_SelectedTab_NonLastTab_Rtl_HideCloseBtn() { + initializeTest(true, false, 3); + StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_1); + // Set mWidth value to 800.f mStripLayoutHelper.onSizeChanged(SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP); - // To make rightmost tab partially hidden value should be gt than (SCREEN_WIDTH - 120.f - - // TAB_WIDTH_2). - when(tabs[4].getDrawX()).thenReturn(710.f); + mStripLayoutHelper.getNewTabButton().setX(NEW_TAB_BTN_X); mStripLayoutHelper.setStripLayoutTabsForTest(tabs); // Act mStripLayoutHelper.tabSelected(1, 3, 0, false); // Assert - // Close button is visible for the rest of the tabs. - Mockito.verify(tabs[0]).setCanShowCloseButton(true, false); - Mockito.verify(tabs[1]).setCanShowCloseButton(true, false); - Mockito.verify(tabs[2]).setCanShowCloseButton(true, false); + // Close btn is hidden for selected tab. + Mockito.verify(tabs[3]).setCanShowCloseButton(false, false); + // Close btn is hidden for all the rest of tabs as well. + Mockito.verify(tabs[0]).setCanShowCloseButton(false, false); + Mockito.verify(tabs[1]).setCanShowCloseButton(false, false); + Mockito.verify(tabs[2]).setCanShowCloseButton(false, false); + Mockito.verify(tabs[4]).setCanShowCloseButton(false, false); + } + + @Test + // Test show selected tab(non-last tab) close button when RTL. + public void testTabSelected_SelectedTab_NonLastTab_Rtl_ShowCloseBtn() { + initializeTest(true, false, 3); + StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_1); + // Set mWidth value to 800.f. + mStripLayoutHelper.onSizeChanged(SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP); + mStripLayoutHelper.getNewTabButton().setX(NEW_TAB_BTN_X); + // getCloseBtnVisibilityThreshold(120.f) - mTabOverlapWidth(28.f) + 1 + when(tabs[3].getDrawX()).thenReturn(93.f); + mStripLayoutHelper.setStripLayoutTabsForTest(tabs); + + // Act + mStripLayoutHelper.tabSelected(1, 3, 0, false); + + // Assert + // Close button is visible for the selected tab. Mockito.verify(tabs[3]).setCanShowCloseButton(true, false); - // Close btn should be hidden for the partially visible edge tab. + // Close button is hidden for the rest of tabs. + Mockito.verify(tabs[0]).setCanShowCloseButton(false, false); + Mockito.verify(tabs[1]).setCanShowCloseButton(false, false); + Mockito.verify(tabs[2]).setCanShowCloseButton(false, false); Mockito.verify(tabs[4]).setCanShowCloseButton(false, false); } @@ -599,7 +579,6 @@ // Setup TabManagementFieldTrial.TAB_STRIP_REDESIGN_ENABLE_FOLIO.setForTesting(true); int tabCount = 4; - TabUiFeatureUtilities.setTabMinWidthForTesting(TAB_WIDTH_MEDIUM); initializeTest(false, false, false, 3, tabCount); mStripLayoutHelper.onSizeChanged(SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP); @@ -617,7 +596,6 @@ // Setup TabManagementFieldTrial.TAB_STRIP_REDESIGN_ENABLE_FOLIO.setForTesting(true); int tabCount = 4; - TabUiFeatureUtilities.setTabMinWidthForTesting(TAB_WIDTH_MEDIUM); initializeTest(true, false, false, 3, tabCount); // Set New tab button position. @@ -634,7 +612,6 @@ // Setup TabManagementFieldTrial.TAB_STRIP_REDESIGN_ENABLE_FOLIO.setForTesting(true); int tabCount = 4; - TabUiFeatureUtilities.setTabMinWidthForTesting(TAB_WIDTH_MEDIUM); when(mModelSelectorBtn.getWidth()).thenReturn(MODEL_SELECTOR_BUTTON_BG_WIDTH_FOLIO); initializeTest(false, true, false, 3, tabCount); mStripLayoutHelper.onSizeChanged(SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP); @@ -655,7 +632,6 @@ // Setup int tabCount = 4; TabManagementFieldTrial.TAB_STRIP_REDESIGN_ENABLE_FOLIO.setForTesting(true); - TabUiFeatureUtilities.setTabMinWidthForTesting(TAB_WIDTH_MEDIUM); initializeTest(true, true, false, 3, tabCount); when(mModelSelectorBtn.getWidth()).thenReturn(MODEL_SELECTOR_BUTTON_BG_WIDTH_FOLIO); mStripLayoutHelper.onSizeChanged(SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP); @@ -671,11 +647,9 @@ } @Test - @Feature("Tab Strip Improvements") public void testScrollOffset_OnResume_StartOnLeft_SelectedRightmostTab() { // Arrange: Initialize tabs with last tab selected. initializeTest(false, true, false, 9, 10); - TabUiFeatureUtilities.setTabMinWidthForTesting(TAB_WIDTH_MEDIUM); StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_MEDIUM, 150.f, 10); mStripLayoutHelper.setStripLayoutTabsForTest(tabs); @@ -684,18 +658,16 @@ mStripLayoutHelper.scrollTabToView(TIMESTAMP, false); - int expectedFinalX = -968; // delta(optimalRight(-940) - scrollOffset(0) + int expectedFinalX = -148; // delta(optimalRight(-120) - scrollOffset(0) // - tabOverlapWidth(28)) + scrollOffset(0) assertEquals(expectedFinalX, mStripLayoutHelper.getScroller().getFinalX()); } @Test - @Feature("Tab Strip Improvements") public void testScrollOffset_OnResume_StartOnLeft_NoModelSelBtn_SelectedRightmostTab() { // Arrange: Initialize tabs with last tab selected. when(mModelSelectorBtn.isVisible()).thenReturn(false); initializeTest(false, true, false, 9, 10); - TabUiFeatureUtilities.setTabMinWidthForTesting(TAB_WIDTH_MEDIUM); StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_MEDIUM, 150.f, 10); mStripLayoutHelper.setStripLayoutTabsForTest(tabs); @@ -704,17 +676,16 @@ mStripLayoutHelper.scrollTabToView(TIMESTAMP, false); - int expectedFinalX = -920; // delta(optimalRight(-892) - scrollOffset(0) + System.out.println(); + int expectedFinalX = -100; // delta(optimalRight(-72) - scrollOffset(0) // - tabOverlapWidth(28)) + scrollOffset(0) assertEquals(expectedFinalX, mStripLayoutHelper.getScroller().getFinalX()); } @Test - @Feature("Tab Strip Improvements") public void testScrollOffset_OnResume_StartOnRight_SelectedLeftmostTab() { // Arrange: Initialize tabs with first tab selected. initializeTest(false, true, false, 0, 10); - TabUiFeatureUtilities.setTabMinWidthForTesting(TAB_WIDTH_MEDIUM); StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_MEDIUM, 150.f, 10); mStripLayoutHelper.setStripLayoutTabsForTest(tabs); mStripLayoutHelper.testSetScrollOffset(-1200); @@ -729,12 +700,10 @@ } @Test - @Feature("Tab Strip Improvements") public void testScrollOffset_OnResume_StartOnRight_NoModelSelBtn_SelectedRightmostTab() { // Arrange: Initialize tabs with first tab selected. when(mModelSelectorBtn.isVisible()).thenReturn(false); initializeTest(false, true, false, 0, 10); - TabUiFeatureUtilities.setTabMinWidthForTesting(TAB_WIDTH_MEDIUM); StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_MEDIUM, 150.f, 10); mStripLayoutHelper.setStripLayoutTabsForTest(tabs); mStripLayoutHelper.testSetScrollOffset(-1200); @@ -749,12 +718,10 @@ } @Test - @Feature("Tab Strip Improvements") public void testScrollOffset_OnOrientationChange_SelectedTabVisible() { // Arrange: Initialize tabs with last tab selected. when(mModelSelectorBtn.isVisible()).thenReturn(false); initializeTest(false, true, false, 9, 10); - TabUiFeatureUtilities.setTabMinWidthForTesting(TAB_WIDTH_MEDIUM); StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_MEDIUM, 150.f, 10); when(tabs[9].isVisible()).thenReturn(true); mStripLayoutHelper.setStripLayoutTabsForTest(tabs); @@ -764,7 +731,7 @@ mStripLayoutHelper.onSizeChanged(SCREEN_WIDTH_LANDSCAPE, SCREEN_HEIGHT, false, TIMESTAMP); // Assert: finalX value before orientation change. - int initialFinalX = -1458; + int initialFinalX = -720; assertEquals(initialFinalX, mStripLayoutHelper.getScroller().getFinalX()); // Act: change orientation. @@ -772,18 +739,16 @@ mStripLayoutHelper.onSizeChanged(SCREEN_WIDTH, SCREEN_HEIGHT, true, TIMESTAMP); // Assert: finalX value after orientation change. - int expectedFinalX = -920; // delta(optimalRight(-892) - tabOverlapWidth(28)) - + int expectedFinalX = -100; // delta(optimalRight(-72) - tabOverlapWidth(28)) - // scrollOffset(1000) + scrollOffset(1000) assertEquals(expectedFinalX, mStripLayoutHelper.getScroller().getFinalX()); } @Test - @Feature("Tab Strip Improvements") public void testScrollOffset_OnOrientationChange_SelectedTabNotVisible() { // Arrange: Initialize tabs with last tab selected. when(mModelSelectorBtn.isVisible()).thenReturn(false); initializeTest(false, true, false, 9, 10); - TabUiFeatureUtilities.setTabMinWidthForTesting(TAB_WIDTH_MEDIUM); StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_MEDIUM, 150.f, 10); when(tabs[9].isVisible()).thenReturn(false); mStripLayoutHelper.setStripLayoutTabsForTest(tabs); @@ -793,7 +758,7 @@ mStripLayoutHelper.onSizeChanged(SCREEN_WIDTH_LANDSCAPE, SCREEN_HEIGHT, false, TIMESTAMP); // Assert: finalX value before orientation change. - int initialFinalX = -1458; + int initialFinalX = -720; assertEquals(initialFinalX, mStripLayoutHelper.getScroller().getFinalX()); // Act: change orientation. @@ -805,7 +770,6 @@ } @Test - @Feature("Tab Strip Improvements") public void testTabSelected_AfterTabClose_SkipsAutoScroll() { initializeTest(false, true, 3); StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_MEDIUM); @@ -821,7 +785,6 @@ } @Test - @Feature("Tab Strip Improvements") public void testTabSelected_AfterSelectedTabClose_SkipsAutoScroll() { initializeTest(false, true, 3); StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_MEDIUM); @@ -854,7 +817,6 @@ } @Test - @Feature("Tab Strip Improvements") public void testTabCreated_RestoredTab_SkipsAutoscroll() { initializeTest(false, true, 3); StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_MEDIUM); @@ -871,7 +833,6 @@ } @Test - @Feature("Tab Strip Improvements") public void testTabCreated_NonRestoredTab_SkipsAutoscroll() { initializeTest(false, true, 3); StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_MEDIUM); @@ -888,9 +849,8 @@ } @Test - @Feature("Tab Strip Improvements") public void testTabCreated_BringSelectedTabToVisibleArea_StartupRestoredUnselectedTab() { - initializeTest(false, false, true, 1, 10); + initializeTest(false, false, true, 1, 11); mStripLayoutHelper.onSizeChanged(SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP); // Set initial scroller position to -500. mStripLayoutHelper.testSetScrollOffset(-500); @@ -899,17 +859,16 @@ // Act: Tab was restored during startup. boolean selected = false; boolean onStartup = true; - mStripLayoutHelper.tabCreated(TIMESTAMP, 11, 11, selected, false, onStartup); + mStripLayoutHelper.tabCreated(TIMESTAMP, 12, 12, selected, false, onStartup); // Assert: We don't scroll to the created tab. The selected tab is not already visible, so - // we scroll to it. Offset = -(1 tab width) = -162. - float expectedOffset = -162f; + // we scroll to it. Offset = -(1 tab width) = -80. + float expectedOffset = -80f; assertEquals("We should scroll to the selected tab", expectedOffset, mStripLayoutHelper.getScrollOffset(), EPSILON); } @Test - @Feature("Tab Strip Improvements") public void testScrollDuration() { initializeTest(false, true, 3); @@ -921,9 +880,8 @@ } @Test - @Feature("Tab Strip Improvements") public void testScrollDuration_Medium() { - initializeTest(false, true, false, 3, 10); + initializeTest(false, true, false, 3, 12); // Act: Set scroll offset between -960 and -1920. mStripLayoutHelper.testSetScrollOffset(-1000); @@ -933,9 +891,8 @@ } @Test - @Feature("Tab Strip Improvements") public void testScrollDuration_Large() { - initializeTest(false, true, false, 3, 15); + initializeTest(false, true, false, 3, 24); // Act: Set scroll offset less than -1920 mStripLayoutHelper.testSetScrollOffset(-2000); @@ -1345,8 +1302,9 @@ mStripLayoutHelper.testSetScrollOffset(0); // Start reorder on tab to the right of groups. 2 margins to left of tab, so should scroll. - // Verify the scroll offset is 2 * (-marginWidth) + startMargin = 2 * -95 + -95 = -285 - float expectedOffset = -285f; + // Verify the scroll offset is 2 * (-marginWidth) + startMargin = 2 * -54 + -54 = -162 + // marginWidth is half of 0.5 * minTabWidth = 108 / 2 = 54. + float expectedOffset = -162f; mStripLayoutHelper.startReorderModeAtIndexForTesting(4); assertEquals("There are margins left of the selected tab, so we should scroll.", expectedOffset, mStripLayoutHelper.getScrollOffset(), EPSILON); @@ -1374,8 +1332,9 @@ mStripLayoutHelper.getScrollOffset(), EPSILON); // Finish animations. - // Verify the scroll offset is 2 * (-marginWidth) + startMargin = 2 * -95 + -95 = -285 - float expectedOffset = -285f; + // Verify the scroll offset is 2 * (-marginWidth) + startMargin = 2 * -54 + -54 = -162 + // marginWidth is half of 0.5 * minTabWidth = 108 / 2 = 54. + float expectedOffset = -162f; mStripLayoutHelper.getRunningAnimatorForTesting().end(); assertEquals("The scroller has finished, so the offset should change.", expectedOffset, mStripLayoutHelper.getScrollOffset(), EPSILON); @@ -1624,7 +1583,7 @@ // Start reorder mode on third tab. Drag between tabs in group. // -300 < -(tabWidth + marginWidth) = -(190 + 95) = -285 mStripLayoutHelper.startReorderModeAtIndexForTesting(2); - float dragDistance = -300f; + float dragDistance = -200f; float startX = mStripLayoutHelper.getLastReorderX(); mStripLayoutHelper.drag(TIMESTAMP, startX + dragDistance, 0f, dragDistance, 0f, 0f, 0f); @@ -1703,13 +1662,11 @@ } @Test - @Feature("Tab Strip Improvements") public void testTabClosing_NoTabResize() { // Arrange - int tabCount = 10; - TabUiFeatureUtilities.setTabMinWidthForTesting(TAB_WIDTH_MEDIUM); - initializeTest(false, false, false, 9, tabCount); - StripLayoutTab[] tabs = getRealStripLayoutTabs(TAB_WIDTH_MEDIUM, tabCount); + int tabCount = 15; + initializeTest(false, false, false, 14, tabCount); + StripLayoutTab[] tabs = getRealStripLayoutTabs(TAB_WIDTH_SMALL, tabCount); mStripLayoutHelper.setStripLayoutTabsForTest(tabs); mStripLayoutHelper.onSizeChanged(SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP); setupForAnimations(); @@ -1717,7 +1674,7 @@ mStripLayoutHelper.updateLayout(TIMESTAMP); // Act: Call on close tab button handler. - mStripLayoutHelper.handleCloseButtonClick(tabs[9], TIMESTAMP); + mStripLayoutHelper.handleCloseButtonClick(tabs[14], TIMESTAMP); // Assert: Animations started. assertTrue("MultiStepAnimations should have started.", @@ -1729,7 +1686,7 @@ runningAnimator.end(); // Assert: Tab is closed and animations are still running. - int expectedTabCount = 9; + int expectedTabCount = 14; assertEquals("Unexpected tabs count.", expectedTabCount, mStripLayoutHelper.getStripLayoutTabs().length); assertTrue("MultiStepAnimations should still be running.", @@ -1740,23 +1697,21 @@ // Assert: Animations completed. The tab width is not resized and drawX does not change. float expectedDrawX = - -423.f; // Since we are focused on the last tab, start tabs are off screen. + -391.f; // Since we are focused on the last tab, start tabs are off screen. StripLayoutTab[] updatedTabs = mStripLayoutHelper.getStripLayoutTabs(); for (StripLayoutTab stripTab : updatedTabs) { - assertEquals("Unexpected tab width after resize.", 156.f, stripTab.getWidth(), 0.0); - assertEquals("Unexpected tab position.", expectedDrawX, stripTab.getDrawX(), 0.0); - expectedDrawX += (TAB_WIDTH_MEDIUM - TAB_OVERLAP_WIDTH); + assertEquals("Unexpected tab width after resize.", 108.f, stripTab.getWidth(), 0); + assertEquals("Unexpected tab position.", expectedDrawX, stripTab.getDrawX(), 0); + expectedDrawX += TAB_WIDTH_SMALL - TAB_OVERLAP_WIDTH; } assertFalse("MultiStepAnimations should have stopped running.", mStripLayoutHelper.isMultiStepCloseAnimationsRunning()); } @Test - @Feature("Tab Strip Improvements") public void testTabClosing_NonLastTab_TabResize() { // Arrange int tabCount = 4; - TabUiFeatureUtilities.setTabMinWidthForTesting(TAB_WIDTH_MEDIUM); initializeTest(false, false, false, 3, tabCount); StripLayoutTab[] tabs = getRealStripLayoutTabs(TAB_WIDTH_MEDIUM, tabCount); mStripLayoutHelper.setStripLayoutTabsForTest(tabs); @@ -1805,7 +1760,7 @@ @Test public void testFlingLeft() { // Arrange - initializeTest(false, false, false, 9, 10); + initializeTest(false, false, false, 11, 12); mStripLayoutHelper.onSizeChanged(SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP); mStripLayoutHelper.updateLayout(TIMESTAMP); mStripLayoutHelper.testSetScrollOffset(-150); @@ -1830,7 +1785,7 @@ @Test public void testFlingRight() { // Arrange - initializeTest(false, false, false, 9, 10); + initializeTest(false, false, false, 10, 11); mStripLayoutHelper.onSizeChanged(SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP); // When updateLayout is called for the first time, bringSelectedTabToVisibleArea() method is // invoked. That also affects the scrollOffset value. So we call updateLayout before @@ -1856,10 +1811,9 @@ } @Test - @Feature("Tab Strip Improvements") public void testDrag_UpdatesScrollOffset_ScrollingStrip() { // Arrange - initializeTest(false, false, false, 9, 10); + initializeTest(false, false, false, 13, 14); mStripLayoutHelper.onSizeChanged(SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP); // When updateLayout is called for the first time, bringSelectedTabToVisibleArea() method is // invoked. That also affects the scrollOffset value. So we call updateLayout before @@ -1880,60 +1834,6 @@ assertFalse(mStripLayoutHelper.isInReorderModeForTesting()); } - @Test - public void testDrag_UpdatesScrollOffset_CascadingStrip() { - // Arrange - ChromeFeatureList.sTabStripImprovements.setForTesting(false); - initializeTest(false, false, false, 0, 10); - mStripLayoutHelper.onSizeChanged(SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP); - // When updateLayout is called for the first time, bringSelectedTabToVisibleArea() method is - // invoked. That also affects the scrollOffset value. So we call updateLayout before - // performing a fling so that bringSelectedTabToVisible area isn't called after the fling. - mStripLayoutHelper.updateLayout(TIMESTAMP); - mStripLayoutHelper.testSetScrollOffset(-250); - - // Act: Drag and update layout. - float dragDeltaX = -200.f; - mStripLayoutHelper.drag( - TIMESTAMP, 374.74f, 24.276f, dragDeltaX, -0.304f, -16.078f, -4.476f); - - // Assert - float expectedOffset = -450; // mScrollOffset + dragDeltaX = -200 - 250 = -450 - assertEquals("Unexpected scroll offset.", expectedOffset, - mStripLayoutHelper.getScrollOffset(), 0.0); - assertFalse("Reorder mode should not enabled when totalY <= 50.", - mStripLayoutHelper.isInReorderModeForTesting()); - } - - @Test - public void testDrag_ReorderMode_CascadingStrip() { - // Arrange - ChromeFeatureList.sTabStripImprovements.setForTesting(false); - initializeTest(false, false, false, 5, 10); - mStripLayoutHelper.onSizeChanged(SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP); - // When updateLayout is called for the first time, bringSelectedTabToVisibleArea() method is - // invoked. That also affects the scrollOffset value. So we call updateLayout before - // performing a fling so that bringSelectedTabToVisible area isn't called after the fling. - mStripLayoutHelper.updateLayout(TIMESTAMP); - mStripLayoutHelper.testSetScrollOffset(-250); - - // Assert: Ensure reorder mode is disabled when starting drag. - assertFalse("Reorder mode should be disabled before drag.", - mStripLayoutHelper.isInReorderModeForTesting()); - - // Act - float dragDeltaX = -200.f; - float totalY = 75.f; // Drag with totalY > 50.f to cross reorder mode threshold. - mStripLayoutHelper.drag(TIMESTAMP, 374.74f, 24.276f, dragDeltaX, -0.304f, -16.078f, totalY); - - // Assert: Reorder mode is enabled. - assertTrue("Reorder mode was not enabled after drag.", - mStripLayoutHelper.isInReorderModeForTesting()); - float expectedOffset = -450; // mScrollOffset + dragDeltaX = -200 - 250 = -450 - assertEquals("Unexpected scroll offset.", expectedOffset, - mStripLayoutHelper.getScrollOffset(), 0.0); - } - private void setupForAnimations() { CompositorAnimationHandler mHandler = new CompositorAnimationHandler(() -> {}); // CompositorAnimationHandler.setTestingMode(true);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripStackerUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripStackerUnitTest.java index 1902463..08487fa 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripStackerUnitTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripStackerUnitTest.java
@@ -9,7 +9,6 @@ import static org.hamcrest.Matchers.is; import static org.mockito.Mockito.when; -import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -19,7 +18,6 @@ import org.chromium.base.test.BaseRobolectricTestRunner; import org.chromium.base.test.util.DisabledTest; -import org.chromium.chrome.browser.flags.ChromeFeatureList; import org.chromium.ui.base.LocalizationUtils; /** Tests for {@link StripStacker}. */ @@ -61,12 +59,6 @@ when(tab.getDrawX()).thenReturn(x); x += TAB_WIDTH; } - setTabStripImprovementFeature(false); - } - - @After - public void tearDown() { - setTabStripImprovementFeature(false); } @Test @@ -85,16 +77,6 @@ float result = mTarget.computeNewTabButtonOffset(mInput, TAB_OVERLAP, STRIP_LEFT_MARGIN, STRIP_RIGHT_MARGIN, STRIP_WIDTH, BUTTON_WIDTH, TOUCH_OFFSET, CACHED_TAB_WIDTH, true); - assertThat("New Tab button offset does not match", result, is(130f)); - } - - @Test - @DisabledTest(message = "https://crbug.com/1385702") - public void testComputeNewTabButtonOffset_withTabStripImprovements() { - setTabStripImprovementFeature(true); - float result = mTarget.computeNewTabButtonOffset(mInput, TAB_OVERLAP, STRIP_LEFT_MARGIN, - STRIP_RIGHT_MARGIN, STRIP_WIDTH, BUTTON_WIDTH, TOUCH_OFFSET, CACHED_TAB_WIDTH, - true); assertThat("New Tab button offset does not match", result, is(35f)); } @@ -102,24 +84,6 @@ public void testComputeNewTabButtonOffsetRTL() { LocalizationUtils.setRtlForTesting(true); float expected_res = 3f; - // Update drawX for RTL = ((mInput.length -1 ) * TAB_WIDTH) + TOUCH_OFFSET + BUTTON_WIDTH - // +expected_res = 4*25 + 5 + 10 +3 - float draw_x = 118f; - for (StripLayoutTab tab : mInput) { - when(tab.getDrawX()).thenReturn(draw_x); - draw_x -= TAB_WIDTH; - } - float result = mTarget.computeNewTabButtonOffset(mInput, TAB_OVERLAP, STRIP_LEFT_MARGIN, - STRIP_RIGHT_MARGIN, STRIP_WIDTH, BUTTON_WIDTH, TOUCH_OFFSET, CACHED_TAB_WIDTH, - true); - assertThat("New Tab button offset does not match", result, is(expected_res)); - } - - @Test - public void testComputeNewTabButtonOffsetRTL_withTabStripImprovements() { - LocalizationUtils.setRtlForTesting(true); - setTabStripImprovementFeature(true); - float expected_res = 3f; // Update idealX for RTL = ((mInput.length -1 ) * TAB_WIDTH) + TOUCH_OFFSET + BUTTON_WIDTH + // expected_res = 4*25 + 5 + 10 + 3 float ideal_x = 118f; @@ -133,10 +97,6 @@ assertThat("New Tab button offset does not match", result, is(expected_res)); } - private void setTabStripImprovementFeature(boolean value) { - ChromeFeatureList.sTabStripImprovements.setForTesting(value); - } - class DummyStacker extends StripStacker { @Override public void setTabOffsets(int selectedIndex, StripLayoutTab[] indexOrderedTabs,
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/tab/RequestDesktopUtilsUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/tab/RequestDesktopUtilsUnitTest.java index 68575af7..7aa89c36 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/tab/RequestDesktopUtilsUnitTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/tab/RequestDesktopUtilsUnitTest.java
@@ -925,6 +925,78 @@ } @Test + public void testMaybeDisableGlobalSetting_FinchParamChanged_ScreenSizeInches() { + // Default-enable the global setting. + Map<String, String> params = new HashMap<>(); + params.put( + RequestDesktopUtils.PARAM_GLOBAL_SETTING_DEFAULT_ON_DISPLAY_SIZE_THRESHOLD_INCHES, + "10.0"); + enableFeatureWithParams(ChromeFeatureList.REQUEST_DESKTOP_SITE_DEFAULTS, params, true); + RequestDesktopUtils.maybeDefaultEnableGlobalSetting( + /*displaySizeInInches*/ 10.5, mProfile, mActivity); + + // Update finch param and initiate downgrade. + params.put( + RequestDesktopUtils.PARAM_GLOBAL_SETTING_DEFAULT_ON_DISPLAY_SIZE_THRESHOLD_INCHES, + "11.0"); + enableFeatureWithParams(ChromeFeatureList.REQUEST_DESKTOP_SITE_DEFAULTS, params, true); + RequestDesktopUtils.maybeDefaultEnableGlobalSetting( + /*displaySizeInInches*/ 10.5, mProfile, mActivity); + enableFeatureWithParams( + ChromeFeatureList.REQUEST_DESKTOP_SITE_DEFAULTS_DOWNGRADE, null, true); + boolean didDisable = RequestDesktopUtils.maybeDisableGlobalSetting(mProfile); + + Assert.assertTrue( + "Desktop site global setting should be disabled on downgrade.", didDisable); + Assert.assertEquals("Desktop site content setting should be set correctly.", + ContentSettingValues.BLOCK, mRdsDefaultValue); + Assert.assertFalse( + "SharedPreference DEFAULT_ENABLED_DESKTOP_SITE_GLOBAL_SETTING should be removed.", + mSharedPreferencesManager.contains( + ChromePreferenceKeys.DEFAULT_ENABLED_DESKTOP_SITE_GLOBAL_SETTING)); + Assert.assertFalse( + "SharedPreference DEFAULT_ENABLE_DESKTOP_SITE_GLOBAL_SETTING_COHORT should be removed.", + mSharedPreferencesManager.contains( + ChromePreferenceKeys.DEFAULT_ENABLE_DESKTOP_SITE_GLOBAL_SETTING_COHORT)); + } + + @Test + public void testMaybeDisableGlobalSetting_FinchParamChanged_ScreenWidthDp() { + // Default-enable the global setting. + Map<String, String> params = new HashMap<>(); + params.put( + RequestDesktopUtils.PARAM_GLOBAL_SETTING_DEFAULT_ON_SMALLEST_SCREEN_WIDTH, "600"); + enableFeatureWithParams(ChromeFeatureList.REQUEST_DESKTOP_SITE_DEFAULTS, params, true); + RequestDesktopUtils.maybeDefaultEnableGlobalSetting( + RequestDesktopUtils.DEFAULT_GLOBAL_SETTING_DEFAULT_ON_DISPLAY_SIZE_THRESHOLD_INCHES, + mProfile, mActivity); + + // Update finch param and initiate downgrade. + params.put( + RequestDesktopUtils.PARAM_GLOBAL_SETTING_DEFAULT_ON_SMALLEST_SCREEN_WIDTH, "800"); + enableFeatureWithParams(ChromeFeatureList.REQUEST_DESKTOP_SITE_DEFAULTS, params, true); + RequestDesktopUtils.maybeDefaultEnableGlobalSetting( + RequestDesktopUtils.DEFAULT_GLOBAL_SETTING_DEFAULT_ON_DISPLAY_SIZE_THRESHOLD_INCHES, + mProfile, mActivity); + enableFeatureWithParams( + ChromeFeatureList.REQUEST_DESKTOP_SITE_DEFAULTS_DOWNGRADE, null, true); + boolean didDisable = RequestDesktopUtils.maybeDisableGlobalSetting(mProfile); + + Assert.assertTrue( + "Desktop site global setting should be disabled on downgrade.", didDisable); + Assert.assertEquals("Desktop site content setting should be set correctly.", + ContentSettingValues.BLOCK, mRdsDefaultValue); + Assert.assertFalse( + "SharedPreference DEFAULT_ENABLED_DESKTOP_SITE_GLOBAL_SETTING should be removed.", + mSharedPreferencesManager.contains( + ChromePreferenceKeys.DEFAULT_ENABLED_DESKTOP_SITE_GLOBAL_SETTING)); + Assert.assertFalse( + "SharedPreference DEFAULT_ENABLE_DESKTOP_SITE_GLOBAL_SETTING_COHORT should be removed.", + mSharedPreferencesManager.contains( + ChromePreferenceKeys.DEFAULT_ENABLE_DESKTOP_SITE_GLOBAL_SETTING_COHORT)); + } + + @Test public void testShouldShowGlobalSettingOptInMessage_ExperimentControlGroup() { when(mTracker.wouldTriggerHelpUI(FeatureConstants.REQUEST_DESKTOP_SITE_OPT_IN_FEATURE)) .thenReturn(true);
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp index ad28356..3d5ffedb 100644 --- a/chrome/app/chromeos_strings.grdp +++ b/chrome/app/chromeos_strings.grdp
@@ -6236,6 +6236,18 @@ <message name="IDS_PARENT_ACCESS_EXTENSION_APPROVALS_DISABLED_SUBTITLE" desc="Text telling the user that an extension cannot be installed because a parent has disallowed it."> Your parent has turned off "Permissions for sites, apps, and extensions" for Chrome. Adding this extension is not allowed. </message> + <message name="IDS_PARENT_ACCESS_EXTENSION_APPROVALS_ADD_EXTENSION_BEFORE_TITLE" desc="Title for the dialog asking a supervised user if they would like to ask for approval to install a new extension."> + Ask your parent to add this extension? + </message> + <message name="IDS_PARENT_ACCESS_EXTENSION_APPROVALS_ADD_EXTENSION_BEFORE_SUBTITLE" desc="Text indicating that a parent must approve the extension for the supervised user to gain access."> + A parent or guardian has to say that it's OK for you to add this extension + </message> + <message name="IDS_PARENT_ACCESS_EXTENSION_APPROVALS_ENABLE_EXTENSION_BEFORE_TITLE" desc="Title for the dialog asking a supervised user if they would like to ask for approval to enable an installed extension."> + Ask your parent to enable this extension? + </message> + <message name="IDS_PARENT_ACCESS_EXTENSION_APPROVALS_ENABLE_EXTENSION_BEFORE_SUBTITLE" desc="Text indicating that a parent must approve the extension for the supervised user to gain access."> + A parent or guardian has to say that it's OK for you to enable this extension + </message> <!-- Strings shared among supervised user experiences --> <message name="IDS_SUPERVISED_USER_ERROR_TITLE" desc="Title for the UI shown to supervised users when an error has occurred.">
diff --git a/chrome/app/chromeos_strings_grdp/IDS_PARENT_ACCESS_EXTENSION_APPROVALS_ADD_EXTENSION_BEFORE_SUBTITLE.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_PARENT_ACCESS_EXTENSION_APPROVALS_ADD_EXTENSION_BEFORE_SUBTITLE.png.sha1 new file mode 100644 index 0000000..017131a1 --- /dev/null +++ b/chrome/app/chromeos_strings_grdp/IDS_PARENT_ACCESS_EXTENSION_APPROVALS_ADD_EXTENSION_BEFORE_SUBTITLE.png.sha1
@@ -0,0 +1 @@ +e464b2a56eace4c01dad1ca10031ecb5a4c0c72d \ No newline at end of file
diff --git a/chrome/app/chromeos_strings_grdp/IDS_PARENT_ACCESS_EXTENSION_APPROVALS_ADD_EXTENSION_BEFORE_TITLE.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_PARENT_ACCESS_EXTENSION_APPROVALS_ADD_EXTENSION_BEFORE_TITLE.png.sha1 new file mode 100644 index 0000000..017131a1 --- /dev/null +++ b/chrome/app/chromeos_strings_grdp/IDS_PARENT_ACCESS_EXTENSION_APPROVALS_ADD_EXTENSION_BEFORE_TITLE.png.sha1
@@ -0,0 +1 @@ +e464b2a56eace4c01dad1ca10031ecb5a4c0c72d \ No newline at end of file
diff --git a/chrome/app/chromeos_strings_grdp/IDS_PARENT_ACCESS_EXTENSION_APPROVALS_ENABLE_EXTENSION_BEFORE_SUBTITLE.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_PARENT_ACCESS_EXTENSION_APPROVALS_ENABLE_EXTENSION_BEFORE_SUBTITLE.png.sha1 new file mode 100644 index 0000000..b62f0928 --- /dev/null +++ b/chrome/app/chromeos_strings_grdp/IDS_PARENT_ACCESS_EXTENSION_APPROVALS_ENABLE_EXTENSION_BEFORE_SUBTITLE.png.sha1
@@ -0,0 +1 @@ +fd1669a881108e995f847161beaaabb9e20832cb \ No newline at end of file
diff --git a/chrome/app/chromeos_strings_grdp/IDS_PARENT_ACCESS_EXTENSION_APPROVALS_ENABLE_EXTENSION_BEFORE_TITLE.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_PARENT_ACCESS_EXTENSION_APPROVALS_ENABLE_EXTENSION_BEFORE_TITLE.png.sha1 new file mode 100644 index 0000000..b62f0928 --- /dev/null +++ b/chrome/app/chromeos_strings_grdp/IDS_PARENT_ACCESS_EXTENSION_APPROVALS_ENABLE_EXTENSION_BEFORE_TITLE.png.sha1
@@ -0,0 +1 @@ +fd1669a881108e995f847161beaaabb9e20832cb \ No newline at end of file
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index d29092d..41ff792 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd
@@ -12940,9 +12940,6 @@ <if expr="not is_android"> <!-- Device Chooser Prompt --> - <message name="IDS_BLUETOOTH_DEVICE_CHOOSER_PROMPT_EXTENSION_NAME" desc="The label that is used to introduce Bluetooth chooser details to the user in a popup when it is from a Chrome extension."> - "<ph name="CHROME_EXTENSION_NAME">$1<ex>Chrome Extension Name</ex></ph>" wants to pair - </message> <message name="IDS_BLUETOOTH_DEVICE_CHOOSER_TURN_ADAPTER_OFF" desc="Text of a link the user can click to get help information when Bluetooth adapter is turned off."> <ph name="TURN_ON_BLUETOOTH_LINK">$1<ex>Turn on Bluetooth</ex></ph> to allow pairing </message> @@ -12971,9 +12968,6 @@ <ph name="DEVICE_NAME">$1<ex>device name</ex></ph> - Paired </message> </if> - <message name="IDS_USB_DEVICE_CHOOSER_PROMPT_EXTENSION_NAME" desc="The label that is used to introduce USB chooser details to the user in a popup when it is from a Chrome extension."> - "<ph name="CHROME_EXTENSION_NAME">$1<ex>Chrome Extension Name</ex></ph>" wants to connect - </message> <message name="IDS_DEVICE_CHOOSER_ACCNAME_COMPATIBLE_DEVICES_LIST" desc="The accessible name for the list of compatible devices, read to screen reader users when navigating to the container."> Compatible devices </message> @@ -13005,12 +12999,9 @@ <!-- Serial port chooser --> <if expr="not is_android"> - <message name="IDS_SERIAL_PORT_CHOOSER_PROMPT_ORIGIN" desc="The label that is used to introduce serial port chooser details to the user in a popup when it is from a website."> + <message name="IDS_SERIAL_PORT_CHOOSER_PROMPT" desc="The label that is used to introduce serial port chooser details to the user in a popup."> <ph name="Origin">$1<ex>www.google.com</ex></ph> wants to connect to a serial port </message> - <message name="IDS_SERIAL_PORT_CHOOSER_PROMPT_EXTENSION_NAME" desc="The label that is used to introduce serial port chooser details to the user in a popup when it is from a Chrome extension."> - "<ph name="CHROME_EXTENSION_NAME">$1<ex>Chrome Extension Name</ex></ph>" wants to connect to a serial port - </message> <message name="IDS_SERIAL_PORT_CHOOSER_NAME_WITH_PATH" desc="User option displaying a human-readable name for a serial port with the device path in parenthesis"> <ph name="FRIENDLY_NAME">$1<ex>Virtual COM Port</ex></ph> (<ph name="DEVICE_PATH">$2<ex>COM1</ex></ph>) </message> @@ -13045,12 +13036,9 @@ <!-- HID (Human Interface Device) chooser --> <if expr="not is_android"> - <message name="IDS_HID_CHOOSER_PROMPT_ORIGIN" desc="The label that is used to introduce the Human Interface Device (HID) chooser details to the user in a popup when it is from a website."> + <message name="IDS_HID_CHOOSER_PROMPT" desc="The label that is used to introduce the Human Interface Device (HID) chooser details to the user in a popup."> <ph name="Origin">$1<ex>www.google.com</ex></ph> wants to connect to a HID device </message> - <message name="IDS_HID_CHOOSER_PROMPT_EXTENSION_NAME" desc="The label that is used to introduce Human Interface Device (HID) chooser details to the user in a popup when it is from a Chrome extension."> - "<ph name="CHROME_EXTENSION_NAME">$1<ex>Chrome Extension Name</ex></ph>" wants to connect to a HID device - </message> <message name="IDS_HID_CHOOSER_ITEM_WITHOUT_NAME" desc="User option displaying the device IDs for a Human Interface Device (HID) without a device name."> Unknown Device (<ph name="DEVICE_ID">$1<ex>1234:abcd</ex></ph>) </message>
diff --git a/chrome/app/generated_resources_grd/IDS_HID_CHOOSER_PROMPT_ORIGIN.png.sha1 b/chrome/app/generated_resources_grd/IDS_HID_CHOOSER_PROMPT.png.sha1 similarity index 100% rename from chrome/app/generated_resources_grd/IDS_HID_CHOOSER_PROMPT_ORIGIN.png.sha1 rename to chrome/app/generated_resources_grd/IDS_HID_CHOOSER_PROMPT.png.sha1
diff --git a/chrome/app/generated_resources_grd/IDS_HID_CHOOSER_PROMPT_EXTENSION_NAME.png.sha1 b/chrome/app/generated_resources_grd/IDS_HID_CHOOSER_PROMPT_EXTENSION_NAME.png.sha1 deleted file mode 100644 index 90a6bb6b..0000000 --- a/chrome/app/generated_resources_grd/IDS_HID_CHOOSER_PROMPT_EXTENSION_NAME.png.sha1 +++ /dev/null
@@ -1 +0,0 @@ -071dbff1f196a88a6bd8c193de1291d1dbdb77dd \ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_SERIAL_PORT_CHOOSER_PROMPT_ORIGIN.png.sha1 b/chrome/app/generated_resources_grd/IDS_SERIAL_PORT_CHOOSER_PROMPT.png.sha1 similarity index 100% rename from chrome/app/generated_resources_grd/IDS_SERIAL_PORT_CHOOSER_PROMPT_ORIGIN.png.sha1 rename to chrome/app/generated_resources_grd/IDS_SERIAL_PORT_CHOOSER_PROMPT.png.sha1
diff --git a/chrome/app/generated_resources_grd/IDS_SERIAL_PORT_CHOOSER_PROMPT_EXTENSION_NAME.png.sha1 b/chrome/app/generated_resources_grd/IDS_SERIAL_PORT_CHOOSER_PROMPT_EXTENSION_NAME.png.sha1 deleted file mode 100644 index e6f0756f..0000000 --- a/chrome/app/generated_resources_grd/IDS_SERIAL_PORT_CHOOSER_PROMPT_EXTENSION_NAME.png.sha1 +++ /dev/null
@@ -1 +0,0 @@ -c0b7a72549b53d5fbbcb195d7b429de21fd1d229 \ No newline at end of file
diff --git a/chrome/app/vector_icons/BUILD.gn b/chrome/app/vector_icons/BUILD.gn index fd5e44a..d605d6e9b 100644 --- a/chrome/app/vector_icons/BUILD.gn +++ b/chrome/app/vector_icons/BUILD.gn
@@ -184,7 +184,9 @@ "webauthn/webauthn_error.icon", "webauthn/webauthn_error_dark.icon", "zoom_minus.icon", + "zoom_minus_chrome_refresh.icon", "zoom_plus.icon", + "zoom_plus_chrome_refresh.icon", ] if (is_mac) {
diff --git a/chrome/app/vector_icons/zoom_minus_chrome_refresh.icon b/chrome/app/vector_icons/zoom_minus_chrome_refresh.icon new file mode 100644 index 0000000..01b8a423 --- /dev/null +++ b/chrome/app/vector_icons/zoom_minus_chrome_refresh.icon
@@ -0,0 +1,134 @@ +// 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. + +CANVAS_DIMENSIONS, 32, +MOVE_TO, 26.27f, 28, +LINE_TO, 17.73f, 19.47f, +CUBIC_TO, 17.07f, 20.02f, 16.3f, 20.46f, 15.43f, 20.77f, +CUBIC_TO, 14.57f, 21.08f, 13.63f, 21.23f, 12.63f, 21.23f, +CUBIC_TO, 10.23f, 21.23f, 8.19f, 20.4f, 6.5f, 18.73f, +CUBIC_TO, 4.83f, 17.04f, 4, 15, 4, 12.6f, +CUBIC_TO, 4, 10.2f, 4.83f, 8.17f, 6.5f, 6.5f, +CUBIC_TO, 8.19f, 4.83f, 10.23f, 4, 12.63f, 4, +CUBIC_TO, 15.03f, 4, 17.07f, 4.83f, 18.73f, 6.5f, +CUBIC_TO, 20.4f, 8.17f, 21.23f, 10.2f, 21.23f, 12.6f, +CUBIC_TO, 21.23f, 13.58f, 21.08f, 14.51f, 20.77f, 15.4f, +CUBIC_TO, 20.46f, 16.27f, 20.02f, 17.04f, 19.47f, 17.73f, +LINE_TO, 28, 26.27f, +LINE_TO, 26.27f, 28, +CLOSE, +MOVE_TO, 12.63f, 18.8f, +CUBIC_TO, 14.34f, 18.8f, 15.8f, 18.2f, 17, 17, +CUBIC_TO, 18.2f, 15.78f, 18.8f, 14.31f, 18.8f, 12.6f, +CUBIC_TO, 18.8f, 10.89f, 18.2f, 9.43f, 17, 8.23f, +CUBIC_TO, 15.8f, 7.03f, 14.34f, 6.43f, 12.63f, 6.43f, +CUBIC_TO, 10.92f, 6.43f, 9.46f, 7.03f, 8.23f, 8.23f, +CUBIC_TO, 7.03f, 9.43f, 6.43f, 10.89f, 6.43f, 12.6f, +CUBIC_TO, 6.43f, 14.31f, 7.03f, 15.78f, 8.23f, 17, +CUBIC_TO, 9.46f, 18.2f, 10.92f, 18.8f, 12.63f, 18.8f, +CLOSE, +MOVE_TO, 9.27f, 13.83f, +V_LINE_TO, 11.37f, +H_LINE_TO, 15.97f, +V_LINE_TO, 13.83f, +H_LINE_TO, 9.27f, +CLOSE, +NEW_PATH + + + +CANVAS_DIMENSIONS, 24, +MOVE_TO, 19.6f, 21, +LINE_TO, 13.3f, 14.7f, +CUBIC_TO, 12.8f, 15.1f, 12.23f, 15.42f, 11.58f, 15.65f, +CUBIC_TO, 10.93f, 15.88f, 10.23f, 16, 9.5f, 16, +CUBIC_TO, 7.68f, 16, 6.14f, 15.38f, 4.88f, 14.13f, +CUBIC_TO, 3.63f, 12.86f, 3, 11.32f, 3, 9.5f, +CUBIC_TO, 3, 7.68f, 3.63f, 6.15f, 4.88f, 4.9f, +CUBIC_TO, 6.14f, 3.63f, 7.68f, 3, 9.5f, 3, +CUBIC_TO, 11.32f, 3, 12.85f, 3.63f, 14.1f, 4.9f, +CUBIC_TO, 15.37f, 6.15f, 16, 7.68f, 16, 9.5f, +CUBIC_TO, 16, 10.23f, 15.88f, 10.93f, 15.65f, 11.58f, +CUBIC_TO, 15.42f, 12.23f, 15.1f, 12.8f, 14.7f, 13.3f, +LINE_TO, 21, 19.6f, +LINE_TO, 19.6f, 21, +CLOSE, +MOVE_TO, 9.5f, 14, +CUBIC_TO, 10.75f, 14, 11.81f, 13.57f, 12.68f, 12.7f, +CUBIC_TO, 13.56f, 11.82f, 14, 10.75f, 14, 9.5f, +CUBIC_TO, 14, 8.25f, 13.56f, 7.19f, 12.68f, 6.33f, +CUBIC_TO, 11.81f, 5.44f, 10.75f, 5, 9.5f, 5, +CUBIC_TO, 8.25f, 5, 7.18f, 5.44f, 6.3f, 6.33f, +CUBIC_TO, 5.43f, 7.19f, 5, 8.25f, 5, 9.5f, +CUBIC_TO, 5, 10.75f, 5.43f, 11.82f, 6.3f, 12.7f, +CUBIC_TO, 7.18f, 13.57f, 8.25f, 14, 9.5f, 14, +CLOSE, +MOVE_TO, 7, 10.5f, +V_LINE_TO, 8.5f, +H_LINE_TO, 12, +V_LINE_TO, 10.5f, +H_LINE_TO, 7, +CLOSE, +NEW_PATH + + +CANVAS_DIMENSIONS, 20, +MOVE_TO, 15.94f, 17, +LINE_TO, 10.96f, 12, +CUBIC_TO, 10.54f, 12.32f, 10.08f, 12.57f, 9.58f, 12.75f, +CUBIC_TO, 9.08f, 12.92f, 8.56f, 13, 8, 13, +CUBIC_TO, 6.61f, 13, 5.43f, 12.51f, 4.46f, 11.54f, +CUBIC_TO, 3.49f, 10.57f, 3, 9.39f, 3, 8, +CUBIC_TO, 3, 6.61f, 3.49f, 5.43f, 4.46f, 4.46f, +CUBIC_TO, 5.43f, 3.49f, 6.61f, 3, 8, 3, +CUBIC_TO, 9.39f, 3, 10.57f, 3.49f, 11.54f, 4.46f, +CUBIC_TO, 12.51f, 5.43f, 13, 6.61f, 13, 8, +CUBIC_TO, 13, 8.56f, 12.91f, 9.08f, 12.73f, 9.58f, +CUBIC_TO, 12.56f, 10.08f, 12.33f, 10.54f, 12.02f, 10.96f, +LINE_TO, 17, 15.94f, +LINE_TO, 15.94f, 17, +CLOSE, +MOVE_TO, 8, 11.5f, +CUBIC_TO, 8.97f, 11.5f, 9.8f, 11.16f, 10.48f, 10.48f, +CUBIC_TO, 11.16f, 9.8f, 11.5f, 8.97f, 11.5f, 8, +CUBIC_TO, 11.5f, 7.03f, 11.16f, 6.2f, 10.48f, 5.52f, +CUBIC_TO, 9.8f, 4.84f, 8.97f, 4.5f, 8, 4.5f, +CUBIC_TO, 7.03f, 4.5f, 6.2f, 4.84f, 5.52f, 5.52f, +CUBIC_TO, 4.84f, 6.2f, 4.5f, 7.03f, 4.5f, 8, +CUBIC_TO, 4.5f, 8.97f, 4.84f, 9.8f, 5.52f, 10.48f, +CUBIC_TO, 6.2f, 11.16f, 7.03f, 11.5f, 8, 11.5f, +CLOSE, +MOVE_TO, 6, 8.75f, +V_LINE_TO, 7.25f, +H_LINE_TO, 10, +V_LINE_TO, 8.75f, +H_LINE_TO, 6, +CLOSE, +NEW_PATH + + +CANVAS_DIMENSIONS, 16, +MOVE_TO, 10.16f, 9.1f, +CUBIC_TO, 11.41f, 7.34f, 11.26f, 4.9f, 9.68f, 3.32f, +CUBIC_TO, 7.92f, 1.56f, 5.07f, 1.56f, 3.32f, 3.32f, +CUBIC_TO, 1.56f, 5.08f, 1.56f, 7.93f, 3.32f, 9.68f, +CUBIC_TO, 4.9f, 11.26f, 7.34f, 11.41f, 9.1f, 10.16f, +LINE_TO, 12.94f, 14, +LINE_TO, 14, 12.94f, +LINE_TO, 10.16f, 9.1f, +CLOSE, +MOVE_TO, 8.62f, 8.62f, +CUBIC_TO, 7.45f, 9.79f, 5.55f, 9.79f, 4.38f, 8.62f, +CUBIC_TO, 3.21f, 7.45f, 3.21f, 5.55f, 4.38f, 4.38f, +CUBIC_TO, 5.55f, 3.21f, 7.45f, 3.21f, 8.62f, 4.38f, +CUBIC_TO, 9.79f, 5.55f, 9.79f, 7.45f, 8.62f, 8.62f, +CLOSE, +NEW_PATH, +MOVE_TO, 8.5f, 6, +H_LINE_TO, 4.5f, +V_LINE_TO, 7, +H_LINE_TO, 8.5f, +V_LINE_TO, 6, +CLOSE, +NEW_PATH
diff --git a/chrome/app/vector_icons/zoom_plus_chrome_refresh.icon b/chrome/app/vector_icons/zoom_plus_chrome_refresh.icon new file mode 100644 index 0000000..1b47f83c --- /dev/null +++ b/chrome/app/vector_icons/zoom_plus_chrome_refresh.icon
@@ -0,0 +1,166 @@ +// 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. + +CANVAS_DIMENSIONS, 32, +MOVE_TO, 26.27f, 28, +LINE_TO, 17.73f, 19.47f, +CUBIC_TO, 17.07f, 20.02f, 16.3f, 20.46f, 15.43f, 20.77f, +CUBIC_TO, 14.57f, 21.08f, 13.63f, 21.23f, 12.63f, 21.23f, +CUBIC_TO, 10.23f, 21.23f, 8.19f, 20.4f, 6.5f, 18.73f, +CUBIC_TO, 4.83f, 17.04f, 4, 15, 4, 12.6f, +CUBIC_TO, 4, 10.2f, 4.83f, 8.17f, 6.5f, 6.5f, +CUBIC_TO, 8.19f, 4.83f, 10.23f, 4, 12.63f, 4, +CUBIC_TO, 15.03f, 4, 17.07f, 4.83f, 18.73f, 6.5f, +CUBIC_TO, 20.4f, 8.17f, 21.23f, 10.2f, 21.23f, 12.6f, +CUBIC_TO, 21.23f, 13.58f, 21.08f, 14.51f, 20.77f, 15.4f, +CUBIC_TO, 20.46f, 16.27f, 20.02f, 17.04f, 19.47f, 17.73f, +LINE_TO, 28, 26.27f, +LINE_TO, 26.27f, 28, +CLOSE, +MOVE_TO, 12.63f, 18.8f, +CUBIC_TO, 14.34f, 18.8f, 15.8f, 18.2f, 17, 17, +CUBIC_TO, 18.2f, 15.78f, 18.8f, 14.31f, 18.8f, 12.6f, +CUBIC_TO, 18.8f, 10.89f, 18.2f, 9.43f, 17, 8.23f, +CUBIC_TO, 15.8f, 7.03f, 14.34f, 6.43f, 12.63f, 6.43f, +CUBIC_TO, 10.92f, 6.43f, 9.46f, 7.03f, 8.23f, 8.23f, +CUBIC_TO, 7.03f, 9.43f, 6.43f, 10.89f, 6.43f, 12.6f, +CUBIC_TO, 6.43f, 14.31f, 7.03f, 15.78f, 8.23f, 17, +CUBIC_TO, 9.46f, 18.2f, 10.92f, 18.8f, 12.63f, 18.8f, +CLOSE, +MOVE_TO, 11.4f, 16.5f, +V_LINE_TO, 13.83f, +H_LINE_TO, 8.7f, +V_LINE_TO, 11.37f, +H_LINE_TO, 11.4f, +V_LINE_TO, 8.7f, +H_LINE_TO, 13.83f, +V_LINE_TO, 11.37f, +H_LINE_TO, 16.53f, +V_LINE_TO, 13.83f, +H_LINE_TO, 13.83f, +V_LINE_TO, 16.5f, +H_LINE_TO, 11.4f, +CLOSE, +NEW_PATH + + +CANVAS_DIMENSIONS, 24, +MOVE_TO, 19.6f, 21, +LINE_TO, 13.3f, 14.7f, +CUBIC_TO, 12.8f, 15.1f, 12.23f, 15.42f, 11.58f, 15.65f, +CUBIC_TO, 10.93f, 15.88f, 10.23f, 16, 9.5f, 16, +CUBIC_TO, 7.68f, 16, 6.14f, 15.38f, 4.88f, 14.13f, +CUBIC_TO, 3.63f, 12.86f, 3, 11.32f, 3, 9.5f, +CUBIC_TO, 3, 7.68f, 3.63f, 6.15f, 4.88f, 4.9f, +CUBIC_TO, 6.14f, 3.63f, 7.68f, 3, 9.5f, 3, +CUBIC_TO, 11.32f, 3, 12.85f, 3.63f, 14.1f, 4.9f, +CUBIC_TO, 15.37f, 6.15f, 16, 7.68f, 16, 9.5f, +CUBIC_TO, 16, 10.23f, 15.88f, 10.93f, 15.65f, 11.58f, +CUBIC_TO, 15.42f, 12.23f, 15.1f, 12.8f, 14.7f, 13.3f, +LINE_TO, 21, 19.6f, +LINE_TO, 19.6f, 21, +CLOSE, +MOVE_TO, 9.5f, 14, +CUBIC_TO, 10.75f, 14, 11.81f, 13.57f, 12.68f, 12.7f, +CUBIC_TO, 13.56f, 11.82f, 14, 10.75f, 14, 9.5f, +CUBIC_TO, 14, 8.25f, 13.56f, 7.19f, 12.68f, 6.33f, +CUBIC_TO, 11.81f, 5.44f, 10.75f, 5, 9.5f, 5, +CUBIC_TO, 8.25f, 5, 7.18f, 5.44f, 6.3f, 6.33f, +CUBIC_TO, 5.43f, 7.19f, 5, 8.25f, 5, 9.5f, +CUBIC_TO, 5, 10.75f, 5.43f, 11.82f, 6.3f, 12.7f, +CUBIC_TO, 7.18f, 13.57f, 8.25f, 14, 9.5f, 14, +CLOSE, +MOVE_TO, 8.5f, 12.5f, +V_LINE_TO, 10.5f, +H_LINE_TO, 6.5f, +V_LINE_TO, 8.5f, +H_LINE_TO, 8.5f, +V_LINE_TO, 6.5f, +H_LINE_TO, 10.5f, +V_LINE_TO, 8.5f, +H_LINE_TO, 12.5f, +V_LINE_TO, 10.5f, +H_LINE_TO, 10.5f, +V_LINE_TO, 12.5f, +H_LINE_TO, 8.5f, +CLOSE, +NEW_PATH + + + +CANVAS_DIMENSIONS, 20, +MOVE_TO, 15.94f, 17, +LINE_TO, 10.96f, 12, +CUBIC_TO, 10.54f, 12.32f, 10.08f, 12.57f, 9.58f, 12.75f, +CUBIC_TO, 9.08f, 12.92f, 8.56f, 13, 8, 13, +CUBIC_TO, 6.61f, 13, 5.43f, 12.51f, 4.46f, 11.54f, +CUBIC_TO, 3.49f, 10.57f, 3, 9.39f, 3, 8, +CUBIC_TO, 3, 6.61f, 3.49f, 5.43f, 4.46f, 4.46f, +CUBIC_TO, 5.43f, 3.49f, 6.61f, 3, 8, 3, +CUBIC_TO, 9.39f, 3, 10.57f, 3.49f, 11.54f, 4.46f, +CUBIC_TO, 12.51f, 5.43f, 13, 6.61f, 13, 8, +CUBIC_TO, 13, 8.56f, 12.91f, 9.08f, 12.73f, 9.58f, +CUBIC_TO, 12.56f, 10.08f, 12.33f, 10.54f, 12.02f, 10.96f, +LINE_TO, 17, 15.94f, +LINE_TO, 15.94f, 17, +CLOSE, +MOVE_TO, 8, 11.5f, +CUBIC_TO, 8.97f, 11.5f, 9.8f, 11.16f, 10.48f, 10.48f, +CUBIC_TO, 11.16f, 9.8f, 11.5f, 8.97f, 11.5f, 8, +CUBIC_TO, 11.5f, 7.03f, 11.16f, 6.2f, 10.48f, 5.52f, +CUBIC_TO, 9.8f, 4.84f, 8.97f, 4.5f, 8, 4.5f, +CUBIC_TO, 7.03f, 4.5f, 6.2f, 4.84f, 5.52f, 5.52f, +CUBIC_TO, 4.84f, 6.2f, 4.5f, 7.03f, 4.5f, 8, +CUBIC_TO, 4.5f, 8.97f, 4.84f, 9.8f, 5.52f, 10.48f, +CUBIC_TO, 6.2f, 11.16f, 7.03f, 11.5f, 8, 11.5f, +CLOSE, +MOVE_TO, 7.25f, 10.25f, +V_LINE_TO, 8.75f, +H_LINE_TO, 5.75f, +V_LINE_TO, 7.25f, +H_LINE_TO, 7.25f, +V_LINE_TO, 5.75f, +H_LINE_TO, 8.75f, +V_LINE_TO, 7.25f, +H_LINE_TO, 10.25f, +V_LINE_TO, 8.75f, +H_LINE_TO, 8.75f, +V_LINE_TO, 10.25f, +H_LINE_TO, 7.25f, +CLOSE, +NEW_PATH + + +CANVAS_DIMENSIONS, 16, +MOVE_TO, 10.16f, 9.1f, +CUBIC_TO, 11.41f, 7.34f, 11.26f, 4.9f, 9.68f, 3.32f, +CUBIC_TO, 7.92f, 1.56f, 5.07f, 1.56f, 3.32f, 3.32f, +CUBIC_TO, 1.56f, 5.08f, 1.56f, 7.93f, 3.32f, 9.68f, +CUBIC_TO, 4.9f, 11.26f, 7.34f, 11.41f, 9.1f, 10.16f, +LINE_TO, 12.94f, 14, +LINE_TO, 14, 12.94f, +LINE_TO, 10.16f, 9.1f, +CLOSE, +MOVE_TO, 8.62f, 8.62f, +CUBIC_TO, 7.45f, 9.79f, 5.55f, 9.79f, 4.38f, 8.62f, +CUBIC_TO, 3.21f, 7.45f, 3.21f, 5.55f, 4.38f, 4.38f, +CUBIC_TO, 5.55f, 3.21f, 7.45f, 3.21f, 8.62f, 4.38f, +CUBIC_TO, 9.79f, 5.55f, 9.79f, 7.45f, 8.62f, 8.62f, +CLOSE, +NEW_PATH, +MOVE_TO, 7, 4.5f, +H_LINE_TO, 6, +V_LINE_TO, 6, +H_LINE_TO, 4.5f, +V_LINE_TO, 7, +H_LINE_TO, 6, +V_LINE_TO, 8.5f, +H_LINE_TO, 7, +V_LINE_TO, 7, +H_LINE_TO, 8.5f, +V_LINE_TO, 6, +H_LINE_TO, 7, +V_LINE_TO, 4.5f, +CLOSE, +NEW_PATH
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index 23e1682..eae1562 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc
@@ -2983,21 +2983,6 @@ #endif // BUILDFLAG(IS_ANDROID) #if BUILDFLAG(IS_ANDROID) -const FeatureEntry::FeatureParam kTabStripImprovementsTabWidthShort[] = { - {"min_tab_width", "108"}}; -const FeatureEntry::FeatureParam kTabStripImprovementsTabWidthMedium[] = { - {"min_tab_width", "156"}}; - -const FeatureEntry::FeatureVariation kTabStripImprovementsTabWidthVariations[] = - { - {"Short Tab Width", kTabStripImprovementsTabWidthShort, - std::size(kTabStripImprovementsTabWidthShort), nullptr}, - {"Medium Tab Width", kTabStripImprovementsTabWidthMedium, - std::size(kTabStripImprovementsTabWidthMedium), nullptr}, -}; -#endif // BUILDFLAG(IS_ANDROID) - -#if BUILDFLAG(IS_ANDROID) const FeatureEntry::FeatureParam kTabStripRedesignFolio[] = { {"enable_folio", "true"}}; const FeatureEntry::FeatureParam kTabStripRedesignDetached[] = { @@ -5736,6 +5721,11 @@ flag_descriptions::kOmniboxGM3SteadyStateHeightDescription, kOsAll, FEATURE_VALUE_TYPE(omnibox::kOmniboxSteadyStateHeight)}, + {"omnibox-gm3-steady-state-text-color", + flag_descriptions::kOmniboxGM3SteadyStateTextColorName, + flag_descriptions::kOmniboxGM3SteadyStateTextColorDescription, kOsAll, + FEATURE_VALUE_TYPE(omnibox::kOmniboxSteadyStateTextColor)}, + {"omnibox-gm3-steady-state-text-style", flag_descriptions::kOmniboxGM3SteadyStateTextStyleName, flag_descriptions::kOmniboxGM3SteadyStateTextStyleDescription, kOsAll, @@ -6470,13 +6460,6 @@ flag_descriptions::kTabEngagementReportingDescription, kOsAndroid, FEATURE_VALUE_TYPE(chrome::android::kTabEngagementReportingAndroid)}, - {"enable-tab-strip-improvements", - flag_descriptions::kTabStripImprovementsAndroidName, - flag_descriptions::kTabStripImprovementsAndroidDescription, kOsAndroid, - FEATURE_WITH_PARAMS_VALUE_TYPE(chrome::android::kTabStripImprovements, - kTabStripImprovementsTabWidthVariations, - "TabStripImprovementsAndroid")}, - {"enable-discover-multi-column", flag_descriptions::kDiscoverFeedMultiColumnAndroidName, flag_descriptions::kDiscoverFeedMultiColumnAndroidDescription, kOsAndroid,
diff --git a/chrome/browser/android/compositor/layer/tab_handle_layer.cc b/chrome/browser/android/compositor/layer/tab_handle_layer.cc index c2a9d3c..f7c49c0 100644 --- a/chrome/browser/android/compositor/layer/tab_handle_layer.cc +++ b/chrome/browser/android/compositor/layer/tab_handle_layer.cc
@@ -161,12 +161,11 @@ const float padding_left = tab_handle_resource->padding().x(); float close_width = close_button_->bounds().width(); - // For the min_tab_width experiments, if close button is not shown, fill + + // If close button is not shown, fill // the remaining space with the title text - if (base::FeatureList::IsEnabled(chrome::android::kTabStripImprovements)) { - if (close_button_alpha == 0.f) { - close_width = 0.f; - } + if (close_button_alpha == 0.f) { + close_width = 0.f; } int divider_y;
diff --git a/chrome/browser/android/compositor/scene_layer/tab_strip_scene_layer.cc b/chrome/browser/android/compositor/scene_layer/tab_strip_scene_layer.cc index 469afca7..d4d3c3c 100644 --- a/chrome/browser/android/compositor/scene_layer/tab_strip_scene_layer.cc +++ b/chrome/browser/android/compositor/scene_layer/tab_strip_scene_layer.cc
@@ -51,12 +51,6 @@ // while the incognito button and left/ride fade stay fixed. Put the new tab // button and tabs in a separate layer placed visually below the others. scrollable_strip_layer_->SetIsDrawable(true); - const bool tab_strip_improvements_enabled = - base::FeatureList::IsEnabled(chrome::android::kTabStripImprovements); - if (!tab_strip_improvements_enabled) { - scrollable_strip_layer_->AddChild(new_tab_button_); - } - tab_strip_layer_->SetIsDrawable(true); tab_strip_layer_->AddChild(scrollable_strip_layer_); @@ -65,12 +59,10 @@ tab_strip_layer_->AddChild(model_selector_button_); tab_strip_layer_->AddChild(model_selector_button_background_); model_selector_button_background_->AddChild(model_selector_button_); - if (tab_strip_improvements_enabled) { - if (tab_strip_redesign_enabled) { - tab_strip_layer_->AddChild(new_tab_button_background_); - } - tab_strip_layer_->AddChild(new_tab_button_); + if (tab_strip_redesign_enabled) { + tab_strip_layer_->AddChild(new_tab_button_background_); } + tab_strip_layer_->AddChild(new_tab_button_); layer()->AddChild(tab_strip_layer_); }
diff --git a/chrome/browser/android/examples/custom_tabs_client/src/java/org/chromium/customtabsclient/MainActivity.java b/chrome/browser/android/examples/custom_tabs_client/src/java/org/chromium/customtabsclient/MainActivity.java index 1488b2b4..f684adf5 100644 --- a/chrome/browser/android/examples/custom_tabs_client/src/java/org/chromium/customtabsclient/MainActivity.java +++ b/chrome/browser/android/examples/custom_tabs_client/src/java/org/chromium/customtabsclient/MainActivity.java
@@ -12,6 +12,8 @@ import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Color; +import android.graphics.Insets; +import android.graphics.Point; import android.graphics.Rect; import android.media.MediaPlayer; import android.net.Uri; @@ -20,13 +22,14 @@ import android.os.Handler; import android.os.Looper; import android.text.TextUtils; -import android.util.DisplayMetrics; import android.util.Log; import android.util.Pair; +import android.view.Display; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; +import android.view.WindowInsets; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.AutoCompleteTextView; @@ -972,12 +975,17 @@ @Px int res = 0; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - Rect rect = this.getWindowManager().getMaximumWindowMetrics().getBounds(); - res = Math.max(rect.width(), rect.height()); + Insets navbarInsets = + getWindowManager().getCurrentWindowMetrics().getWindowInsets().getInsets( + WindowInsets.Type.navigationBars() | WindowInsets.Type.displayCutout()); + int navbarWidth = navbarInsets.left + navbarInsets.right; + Rect windowBounds = getWindowManager().getCurrentWindowMetrics().getBounds(); + res = windowBounds.width() - navbarWidth; } else { - DisplayMetrics displayMetrics = new DisplayMetrics(); - this.getWindowManager().getDefaultDisplay().getRealMetrics(displayMetrics); - res = Math.max(displayMetrics.widthPixels, displayMetrics.heightPixels); + Display display = getWindowManager().getDefaultDisplay(); + Point size = new Point(); + display.getSize(size); + res = size.x; } return res; }
diff --git a/chrome/browser/apps/almanac_api_client/BUILD.gn b/chrome/browser/apps/almanac_api_client/BUILD.gn index d421570e..c68a041 100644 --- a/chrome/browser/apps/almanac_api_client/BUILD.gn +++ b/chrome/browser/apps/almanac_api_client/BUILD.gn
@@ -21,6 +21,7 @@ "//chrome/browser/apps:user_type_filter", "//chrome/browser/profiles:profile", "//chrome/common:channel_info", + "//chromeos/ash/components/system", "//chromeos/version", "//components/language/core/browser", "//components/prefs",
diff --git a/chrome/browser/apps/almanac_api_client/device_info_manager.cc b/chrome/browser/apps/almanac_api_client/device_info_manager.cc index afb4dfd..0717d16 100644 --- a/chrome/browser/apps/almanac_api_client/device_info_manager.cc +++ b/chrome/browser/apps/almanac_api_client/device_info_manager.cc
@@ -5,6 +5,7 @@ #include "chrome/browser/apps/almanac_api_client/device_info_manager.h" #include "base/functional/callback.h" +#include "base/strings/string_piece.h" #include "base/strings/string_util.h" #include "base/task/task_traits.h" #include "base/task/thread_pool.h" @@ -12,10 +13,12 @@ #include "chrome/browser/browser_process.h" #include "chrome/browser/profiles/profile.h" #include "chrome/common/channel_info.h" +#include "chromeos/ash/components/system/statistics_provider.h" #include "chromeos/version/version_loader.h" #include "components/language/core/browser/pref_names.h" #include "components/prefs/pref_service.h" #include "components/version_info/version_info.h" +#include "third_party/abseil-cpp/absl/types/optional.h" namespace apps { @@ -36,6 +39,7 @@ // - version_info.ash_chrome // - user_type // - channel +// - hardware_id // The method then asynchronously populates: // - version_info.platform (OnPlatformVersionNumber) // - model (OnModelInfo) @@ -48,6 +52,12 @@ device_info.user_type = apps::DetermineUserType(profile_); device_info.version_info.channel = chrome::GetChannel(); + ash::system::StatisticsProvider* provider = + ash::system::StatisticsProvider::GetInstance(); + absl::optional<base::StringPiece> hwid = + provider->GetMachineStatistic(ash::system::kHardwareClassKey); + device_info.hardware_id = std::string(hwid.value_or("unknown")); + // Locale PrefService* prefs = profile_->GetPrefs(); DCHECK(prefs); @@ -89,6 +99,7 @@ os << "Device Info: " << std::endl; os << "- Board: " << device_info.board << std::endl; os << "- Model: " << device_info.model << std::endl; + os << "- Hardware ID: " << device_info.hardware_id << std::endl; os << "- User Type: " << device_info.user_type << std::endl; os << "- Locale: " << device_info.locale << std::endl; os << device_info.version_info;
diff --git a/chrome/browser/apps/almanac_api_client/device_info_manager.h b/chrome/browser/apps/almanac_api_client/device_info_manager.h index a9b9589..a10194f 100644 --- a/chrome/browser/apps/almanac_api_client/device_info_manager.h +++ b/chrome/browser/apps/almanac_api_client/device_info_manager.h
@@ -41,6 +41,11 @@ // The model of the device. e.g. "taniks" std::string model; + // The HWID which identifies the hardware configuration of the device. Set to + // "unknown" if not running on a ChromeOS device. e.g. + // "REDRIX-CLQY C4B-G4H-D3D-U7F-X54-I9N". + std::string hardware_id; + // The user type of the profile currently running. e.g. "unmanaged" std::string user_type;
diff --git a/chrome/browser/apps/almanac_api_client/device_info_manager_unittest.cc b/chrome/browser/apps/almanac_api_client/device_info_manager_unittest.cc index 816ec62..4e1ece8 100644 --- a/chrome/browser/apps/almanac_api_client/device_info_manager_unittest.cc +++ b/chrome/browser/apps/almanac_api_client/device_info_manager_unittest.cc
@@ -11,6 +11,8 @@ #include "chrome/browser/browser_process.h" #include "chrome/common/channel_info.h" #include "chrome/test/base/testing_profile.h" +#include "chromeos/ash/components/system/fake_statistics_provider.h" +#include "chromeos/ash/components/system/statistics_provider.h" #include "components/language/core/browser/pref_names.h" #include "components/prefs/pref_service.h" #include "content/public/test/browser_task_environment.h" @@ -28,12 +30,16 @@ DeviceInfoManager* device_info_manager() { return device_info_manager_.get(); } + ash::system::FakeStatisticsProvider* statistics_provider() { + return &fake_statistics_provider_; + } private: content::BrowserTaskEnvironment task_environment_; TestingProfile profile_; std::unique_ptr<DeviceInfoManager> device_info_manager_; + ash::system::ScopedFakeStatisticsProvider fake_statistics_provider_; }; TEST_F(DeviceInfoManagerTest, CheckDeviceInfo) { @@ -43,6 +49,9 @@ )"; base::test::ScopedChromeOSVersionInfo version(kLsbRelease, base::Time()); + statistics_provider()->SetMachineStatistic(ash::system::kHardwareClassKey, + "FOOBAR D0G-F4N-C1UB"); + static constexpr char kTestLocale[] = "test_locale"; profile()->GetPrefs()->SetString(language::prefs::kApplicationLocale, kTestLocale); @@ -59,6 +68,7 @@ ASSERT_FALSE(device_info.version_info.ash_chrome.empty()); ASSERT_EQ(device_info.version_info.platform, "123.4.5"); ASSERT_EQ(device_info.version_info.channel, chrome::GetChannel()); + ASSERT_EQ(device_info.hardware_id, "FOOBAR D0G-F4N-C1UB"); ASSERT_EQ(device_info.locale, kTestLocale); }
diff --git a/chrome/browser/apps/almanac_api_client/proto/client_context.proto b/chrome/browser/apps/almanac_api_client/proto/client_context.proto index 9b97167..4498d44 100644 --- a/chrome/browser/apps/almanac_api_client/proto/client_context.proto +++ b/chrome/browser/apps/almanac_api_client/proto/client_context.proto
@@ -63,8 +63,8 @@ // The ChromeOS version information. optional Versions versions = 4; - // The SKU identifier for the device sending the request. - optional string sku_id = 5; + // The HWID identifier for the device sending the request. + optional string hardware_id = 5; } // Context about the user of the client device making this request.
diff --git a/chrome/browser/apps/app_preload_service/app_preload_server_connector.cc b/chrome/browser/apps/app_preload_service/app_preload_server_connector.cc index 1920017..2435e68 100644 --- a/chrome/browser/apps/app_preload_service/app_preload_server_connector.cc +++ b/chrome/browser/apps/app_preload_service/app_preload_server_connector.cc
@@ -103,6 +103,7 @@ info.version_info.ash_chrome); device_context->mutable_versions()->set_chrome_os_platform( info.version_info.platform); + device_context->set_hardware_id(info.hardware_id); apps::proto::ClientUserContext* user_context = request_proto.mutable_user_context();
diff --git a/chrome/browser/apps/app_preload_service/app_preload_server_connector_unittest.cc b/chrome/browser/apps/app_preload_service/app_preload_server_connector_unittest.cc index b61c74b2..955128b 100644 --- a/chrome/browser/apps/app_preload_service/app_preload_server_connector_unittest.cc +++ b/chrome/browser/apps/app_preload_service/app_preload_server_connector_unittest.cc
@@ -57,6 +57,7 @@ DeviceInfo device_info; device_info.board = "brya"; device_info.model = "taniks"; + device_info.hardware_id = "FOOBAR D0G-F4N-C1UB"; device_info.user_type = "unmanaged"; device_info.version_info.ash_chrome = "10.10.10"; device_info.version_info.platform = "12345.0.0"; @@ -98,6 +99,7 @@ EXPECT_EQ(request.device_context().versions().chrome_ash(), "10.10.10"); EXPECT_EQ(request.device_context().versions().chrome_os_platform(), "12345.0.0"); + EXPECT_EQ(request.device_context().hardware_id(), "FOOBAR D0G-F4N-C1UB"); } TEST_F(AppPreloadServerConnectorTest, GetAppsForFirstLoginSuccessfulResponse) {
diff --git a/chrome/browser/apps/app_preload_service/app_preload_service_unittest.cc b/chrome/browser/apps/app_preload_service/app_preload_service_unittest.cc index 20d43a3..dbfe6f7 100644 --- a/chrome/browser/apps/app_preload_service/app_preload_service_unittest.cc +++ b/chrome/browser/apps/app_preload_service/app_preload_service_unittest.cc
@@ -20,6 +20,7 @@ #include "chrome/browser/web_applications/test/web_app_install_test_utils.h" #include "chrome/common/chrome_features.h" #include "chrome/test/base/testing_profile.h" +#include "chromeos/ash/components/system/fake_statistics_provider.h" #include "components/prefs/pref_service.h" #include "components/services/app_service/public/cpp/app_registry_cache.h" #include "components/services/app_service/public/cpp/app_update.h" @@ -80,6 +81,7 @@ base::test::ScopedFeatureList scoped_feature_list_; std::unique_ptr<TestingProfile> profile_; user_manager::ScopedUserManager scoped_user_manager_; + ash::system::ScopedFakeStatisticsProvider fake_statistics_provider_; }; TEST_F(AppPreloadServiceTest, ServiceAccessPerProfile) {
diff --git a/chrome/browser/apps/guest_view/web_view_browsertest.cc b/chrome/browser/apps/guest_view/web_view_browsertest.cc index c7a0f6c..8d615be 100644 --- a/chrome/browser/apps/guest_view/web_view_browsertest.cc +++ b/chrome/browser/apps/guest_view/web_view_browsertest.cc
@@ -529,7 +529,7 @@ std::unique_ptr<base::RunLoop> run_loop_; }; -class WebViewTestBase : public extensions::PlatformAppBrowserTest { +class WebViewTest : public extensions::PlatformAppBrowserTest { protected: void SetUp() override { if (UsesFakeSpeech()) { @@ -675,19 +675,19 @@ return; } embedded_test_server()->RegisterRequestHandler(base::BindRepeating( - &WebViewTestBase::RedirectResponseHandler, kRedirectResponsePath, + &WebViewTest::RedirectResponseHandler, kRedirectResponsePath, embedded_test_server()->GetURL(kRedirectResponseFullPath))); embedded_test_server()->RegisterRequestHandler(base::BindRepeating( - &WebViewTestBase::EmptyResponseHandler, kEmptyResponsePath)); + &WebViewTest::EmptyResponseHandler, kEmptyResponsePath)); embedded_test_server()->RegisterRequestHandler(base::BindRepeating( - &WebViewTestBase::UserAgentResponseHandler, + &WebViewTest::UserAgentResponseHandler, kUserAgentRedirectResponsePath, embedded_test_server()->GetURL(kRedirectResponseFullPath))); embedded_test_server()->RegisterRequestHandler(base::BindRepeating( - &WebViewTestBase::CacheControlResponseHandler, kCacheResponsePath)); + &WebViewTest::CacheControlResponseHandler, kCacheResponsePath)); EmbeddedTestServerAcceptConnections(); } @@ -826,11 +826,11 @@ return manager; } - WebViewTestBase() : guest_view_(nullptr), embedder_web_contents_(nullptr) { + WebViewTest() : guest_view_(nullptr), embedder_web_contents_(nullptr) { GuestViewManager::set_factory_for_testing(&factory_); } - ~WebViewTestBase() override = default; + ~WebViewTest() override = default; extensions::IdentifiabilityMetricsTestHelper identifiability_metrics_test_helper_; @@ -855,64 +855,12 @@ raw_ptr<content::WebContents, DanglingUntriaged> embedder_web_contents_; }; -class WebViewGuestSiteIsolationTest : public WebViewTestBase { - public: - explicit WebViewGuestSiteIsolationTest( - bool site_isolation_for_guests_enabled) { - scoped_feature_list_.InitWithFeatureState( - features::kSiteIsolationForGuests, site_isolation_for_guests_enabled); - } - ~WebViewGuestSiteIsolationTest() override = default; - - private: - base::test::ScopedFeatureList scoped_feature_list_; -}; - -class WebViewTest : public WebViewGuestSiteIsolationTest, - public testing::WithParamInterface<bool> { - public: - WebViewTest() - : WebViewGuestSiteIsolationTest( - /*site_isolation_for_guests_enabled=*/GetParam()) {} - - // Provides meaningful param names instead of /0 and /1. - static std::string DescribeParams( - const testing::TestParamInfo<ParamType>& info) { - return info.param ? "SiteIsolationForGuestsEnabled" - : "SiteIsolationForGuestsDisabled"; - } - - private: - base::test::ScopedFeatureList scoped_feature_list_; -}; - -INSTANTIATE_TEST_SUITE_P(WebViewTests, - WebViewTest, - testing::Bool(), - WebViewTest::DescribeParams); - // The following test suites are created to group tests based on specific // features of <webview>. using WebViewSizeTest = WebViewTest; -INSTANTIATE_TEST_SUITE_P(WebViewTests, - WebViewSizeTest, - testing::Bool(), - WebViewTest::DescribeParams); using WebViewVisibilityTest = WebViewTest; -INSTANTIATE_TEST_SUITE_P(WebViewTests, - WebViewVisibilityTest, - testing::Bool(), - WebViewTest::DescribeParams); using WebViewSpeechAPITest = WebViewTest; -INSTANTIATE_TEST_SUITE_P(WebViewTests, - WebViewSpeechAPITest, - testing::Bool(), - WebViewTest::DescribeParams); using WebViewAccessibilityTest = WebViewTest; -INSTANTIATE_TEST_SUITE_P(WebViewTests, - WebViewAccessibilityTest, - testing::Bool(), - WebViewTest::DescribeParams); // Used to test that enterprise policy can revert MPArch related changes. For // ease of testing, instead of further parameterizing the tests which actually @@ -952,42 +900,22 @@ extensions::AreWebviewMPArchBehaviorsEnabled(browser()->profile())); } -class WebViewNewWindowTest - : public WebViewTestBase, - public testing::WithParamInterface<testing::tuple<bool, bool>> { +class WebViewNewWindowTest : public WebViewTest, + public testing::WithParamInterface<bool> { public: WebViewNewWindowTest() { - auto [is_site_isolation_enabled, mparch_newwindow_restriction] = GetParam(); - std::vector<base::test::FeatureRef> enabled_features, disabled_features; - if (is_site_isolation_enabled) { - enabled_features.push_back(features::kSiteIsolationForGuests); - } else { - disabled_features.push_back(features::kSiteIsolationForGuests); - } - - if (mparch_newwindow_restriction) { - enabled_features.push_back( - extensions_features::kWebviewTagMPArchBehavior); - } else { - disabled_features.push_back( - extensions_features::kWebviewTagMPArchBehavior); - } - - scoped_feature_list_.InitWithFeatures(std::move(enabled_features), - std::move(disabled_features)); + scoped_feature_list_.InitWithFeatureState( + extensions_features::kWebviewTagMPArchBehavior, /*enabled=*/GetParam()); } ~WebViewNewWindowTest() override = default; static std::string DescribeParams( const testing::TestParamInfo<ParamType>& info) { - auto [is_site_isolation_enabled, mparch_newwindow_restriction] = info.param; - return base::StringPrintf( - "SiteIsolationForGuests%s_NewWindow%s", - is_site_isolation_enabled ? "Enabled" : "Disabled", - mparch_newwindow_restriction ? "Restricted" : "Legacy"); + return base::StringPrintf("NewWindow%s", + info.param ? "Restricted" : "Legacy"); } - bool IsNewWindowRestricted() { return testing::get<1>(GetParam()); } + bool IsNewWindowRestricted() { return GetParam(); } private: base::test::ScopedFeatureList scoped_feature_list_; @@ -995,7 +923,7 @@ INSTANTIATE_TEST_SUITE_P(WebViewNewWindowTests, WebViewNewWindowTest, - testing::Combine(testing::Bool(), testing::Bool()), + testing::Bool(), WebViewNewWindowTest::DescribeParams); class WebViewDPITest : public WebViewTest { @@ -1008,10 +936,6 @@ static float scale() { return 2.0f; } }; -INSTANTIATE_TEST_SUITE_P(WebViewTests, - WebViewDPITest, - testing::Bool(), - WebViewTest::DescribeParams); class WebContentsAudioMutedObserver : public content::WebContentsObserver { public: @@ -1066,7 +990,7 @@ std::unique_ptr<base::RunLoop> run_loop_; }; -IN_PROC_BROWSER_TEST_P(WebViewTest, AudibilityStatePropagates) { +IN_PROC_BROWSER_TEST_F(WebViewTest, AudibilityStatePropagates) { ASSERT_TRUE(StartEmbeddedTestServer()); // For serving guest audio. LoadAppWithGuest("web_view/simple"); @@ -1108,7 +1032,7 @@ EXPECT_FALSE(guest->IsCurrentlyAudible()); } -IN_PROC_BROWSER_TEST_P(WebViewTest, WebViewRespectsInsets) { +IN_PROC_BROWSER_TEST_F(WebViewTest, WebViewRespectsInsets) { LoadAppWithGuest("web_view/simple"); content::RenderWidgetHostView* guest_host_view = @@ -1124,7 +1048,7 @@ EXPECT_EQ(expected.size(), size_after); } -IN_PROC_BROWSER_TEST_P(WebViewTest, AudioMutesWhileAttached) { +IN_PROC_BROWSER_TEST_F(WebViewTest, AudioMutesWhileAttached) { LoadAppWithGuest("web_view/simple"); content::WebContents* embedder = GetEmbedderWebContents(); @@ -1142,7 +1066,7 @@ EXPECT_FALSE(guest->IsAudioMuted()); } -IN_PROC_BROWSER_TEST_P(WebViewTest, AudioMutesOnAttach) { +IN_PROC_BROWSER_TEST_F(WebViewTest, AudioMutesOnAttach) { LoadAndLaunchPlatformApp("web_view/app_creates_webview", "WebViewTest.LAUNCHED"); content::WebContents* embedder = GetEmbedderWebContents(); @@ -1162,7 +1086,7 @@ EXPECT_TRUE(guest->IsAudioMuted()); } -IN_PROC_BROWSER_TEST_P(WebViewTest, AudioStateJavascriptAPI) { +IN_PROC_BROWSER_TEST_F(WebViewTest, AudioStateJavascriptAPI) { base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( switches::kAutoplayPolicy, switches::autoplay::kNoUserGestureRequiredPolicy); @@ -1174,7 +1098,7 @@ } // Test that WebView does not override autoplay policy. -IN_PROC_BROWSER_TEST_P(WebViewTest, AutoplayPolicy) { +IN_PROC_BROWSER_TEST_F(WebViewTest, AutoplayPolicy) { base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( switches::kAutoplayPolicy, switches::autoplay::kDocumentUserActivationRequiredPolicy); @@ -1186,7 +1110,7 @@ } // This test exercises the webview spatial navigation API -IN_PROC_BROWSER_TEST_P(WebViewTest, SpatialNavigationJavascriptAPI) { +IN_PROC_BROWSER_TEST_F(WebViewTest, SpatialNavigationJavascriptAPI) { base::CommandLine::ForCurrentProcess()->AppendSwitch( switches::kEnableSpatialNavigation); @@ -1239,7 +1163,7 @@ // This test verifies that hiding the guest triggers visibility change // notifications. -IN_PROC_BROWSER_TEST_P(WebViewVisibilityTest, GuestVisibilityChanged) { +IN_PROC_BROWSER_TEST_F(WebViewVisibilityTest, GuestVisibilityChanged) { LoadAppWithGuest("web_view/visibility_changed"); base::RunLoop run_loop; @@ -1253,7 +1177,7 @@ } // This test verifies that hiding the embedder also hides the guest. -IN_PROC_BROWSER_TEST_P(WebViewVisibilityTest, EmbedderVisibilityChanged) { +IN_PROC_BROWSER_TEST_F(WebViewVisibilityTest, EmbedderVisibilityChanged) { LoadAppWithGuest("web_view/visibility_changed"); base::RunLoop run_loop; @@ -1268,7 +1192,7 @@ // This test verifies that reloading the embedder reloads the guest (and doest // not crash). -IN_PROC_BROWSER_TEST_P(WebViewTest, ReloadEmbedder) { +IN_PROC_BROWSER_TEST_F(WebViewTest, ReloadEmbedder) { // Just load a guest from other test, we do not want to add a separate // platform_app for this test. LoadAppWithGuest("web_view/visibility_changed"); @@ -1281,21 +1205,21 @@ // This test ensures JavaScript errors ("Cannot redefine property") do not // happen when a <webview> is removed from DOM and added back. -IN_PROC_BROWSER_TEST_P(WebViewTest, AddRemoveWebView_AddRemoveWebView) { +IN_PROC_BROWSER_TEST_F(WebViewTest, AddRemoveWebView_AddRemoveWebView) { ASSERT_TRUE(StartEmbeddedTestServer()); // For serving guest pages. ASSERT_TRUE(RunExtensionTest("platform_apps/web_view/addremove", {.launch_as_platform_app = true})) << message_; } -IN_PROC_BROWSER_TEST_P(WebViewSizeTest, AutoSize) { +IN_PROC_BROWSER_TEST_F(WebViewSizeTest, AutoSize) { ASSERT_TRUE(RunExtensionTest("platform_apps/web_view/autosize", {.launch_as_platform_app = true})) << message_; } // Test for http://crbug.com/419611. -IN_PROC_BROWSER_TEST_P(WebViewTest, DisplayNoneSetSrc) { +IN_PROC_BROWSER_TEST_F(WebViewTest, DisplayNoneSetSrc) { LoadAndLaunchPlatformApp("web_view/display_none_set_src", "WebViewTest.LAUNCHED"); // Navigate the guest while it's in "display: none" state. @@ -1313,65 +1237,65 @@ // Checks that {allFrames: true} injects script correctly to subframes // inside <webview>. -IN_PROC_BROWSER_TEST_P(WebViewTest, ExecuteScript) { +IN_PROC_BROWSER_TEST_F(WebViewTest, ExecuteScript) { ASSERT_TRUE(RunExtensionTest( "platform_apps/web_view/common", {.custom_arg = "execute_script", .launch_as_platform_app = true})) << message_; } -IN_PROC_BROWSER_TEST_P(WebViewTest, ExecuteCode) { +IN_PROC_BROWSER_TEST_F(WebViewTest, ExecuteCode) { ASSERT_TRUE(RunExtensionTest( "platform_apps/web_view/common", {.custom_arg = "execute_code", .launch_as_platform_app = true})) << message_; } -IN_PROC_BROWSER_TEST_P(WebViewSizeTest, Shim_TestAutosizeAfterNavigation) { +IN_PROC_BROWSER_TEST_F(WebViewSizeTest, Shim_TestAutosizeAfterNavigation) { TestHelper("testAutosizeAfterNavigation", "web_view/shim", NO_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestAllowTransparencyAttribute) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestAllowTransparencyAttribute) { TestHelper("testAllowTransparencyAttribute", "web_view/shim", NO_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewDPITest, Shim_TestAutosizeHeight) { +IN_PROC_BROWSER_TEST_F(WebViewDPITest, Shim_TestAutosizeHeight) { TestHelper("testAutosizeHeight", "web_view/shim", NO_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewSizeTest, Shim_TestAutosizeHeight) { +IN_PROC_BROWSER_TEST_F(WebViewSizeTest, Shim_TestAutosizeHeight) { TestHelper("testAutosizeHeight", "web_view/shim", NO_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewDPITest, Shim_TestAutosizeBeforeNavigation) { +IN_PROC_BROWSER_TEST_F(WebViewDPITest, Shim_TestAutosizeBeforeNavigation) { TestHelper("testAutosizeBeforeNavigation", "web_view/shim", NO_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewSizeTest, Shim_TestAutosizeBeforeNavigation) { +IN_PROC_BROWSER_TEST_F(WebViewSizeTest, Shim_TestAutosizeBeforeNavigation) { TestHelper("testAutosizeBeforeNavigation", "web_view/shim", NO_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewDPITest, Shim_TestAutosizeRemoveAttributes) { +IN_PROC_BROWSER_TEST_F(WebViewDPITest, Shim_TestAutosizeRemoveAttributes) { TestHelper("testAutosizeRemoveAttributes", "web_view/shim", NO_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewSizeTest, Shim_TestAutosizeRemoveAttributes) { +IN_PROC_BROWSER_TEST_F(WebViewSizeTest, Shim_TestAutosizeRemoveAttributes) { TestHelper("testAutosizeRemoveAttributes", "web_view/shim", NO_TEST_SERVER); } // This test is disabled due to being flaky. http://crbug.com/282116 -IN_PROC_BROWSER_TEST_P(WebViewSizeTest, +IN_PROC_BROWSER_TEST_F(WebViewSizeTest, DISABLED_Shim_TestAutosizeWithPartialAttributes) { TestHelper("testAutosizeWithPartialAttributes", "web_view/shim", NO_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestAPIMethodExistence) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestAPIMethodExistence) { TestHelper("testAPIMethodExistence", "web_view/shim", NO_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestCustomElementCallbacksInaccessible) { TestHelper("testCustomElementCallbacksInaccessible", "web_view/shim", NO_TEST_SERVER); @@ -1379,23 +1303,23 @@ // Tests the existence of WebRequest API event objects on the request // object, on the webview element, and hanging directly off webview. -IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestWebRequestAPIExistence) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestWebRequestAPIExistence) { TestHelper("testWebRequestAPIExistence", "web_view/shim", NO_TEST_SERVER); } // Tests that addListener call succeeds on webview's WebRequest API events. -IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestWebRequestAPIAddListener) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestWebRequestAPIAddListener) { TestHelper("testWebRequestAPIAddListener", "web_view/shim", NO_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestWebRequestAPIErrorOccurred) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestWebRequestAPIErrorOccurred) { TestHelper("testWebRequestAPIErrorOccurred", "web_view/shim", NO_TEST_SERVER); } #if defined(USE_AURA) // Test validates that select tag can be shown and hidden in webview safely // using quick touch. -IN_PROC_BROWSER_TEST_P(WebViewTest, SelectShowHide) { +IN_PROC_BROWSER_TEST_F(WebViewTest, SelectShowHide) { LoadAppWithGuest("web_view/select"); content::WebContents* embedder_contents = GetFirstAppWindowWebContents(); @@ -1428,23 +1352,23 @@ } #endif -IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestChromeExtensionURL) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestChromeExtensionURL) { TestHelper("testChromeExtensionURL", "web_view/shim", NO_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestChromeExtensionRelativePath) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestChromeExtensionRelativePath) { TestHelper("testChromeExtensionRelativePath", "web_view/shim", NO_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestContentInitiatedNavigationToDataUrlBlocked) { TestHelper("testContentInitiatedNavigationToDataUrlBlocked", "web_view/shim", NO_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestDisplayNoneWebviewLoad) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestDisplayNoneWebviewLoad) { TestHelper("testDisplayNoneWebviewLoad", "web_view/shim", NO_TEST_SERVER); } @@ -1457,85 +1381,85 @@ Shim_TestDisplayNoneWebviewRemoveChild #endif // Flaky on most desktop platforms: https://crbug.com/1115106. -IN_PROC_BROWSER_TEST_P(WebViewTest, +IN_PROC_BROWSER_TEST_F(WebViewTest, MAYBE_Shim_TestDisplayNoneWebviewRemoveChild) { TestHelper("testDisplayNoneWebviewRemoveChild", "web_view/shim", NO_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestDisplayBlock) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestDisplayBlock) { TestHelper("testDisplayBlock", "web_view/shim", NO_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestInlineScriptFromAccessibleResources) { TestHelper("testInlineScriptFromAccessibleResources", "web_view/shim", NO_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestInvalidChromeExtensionURL) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestInvalidChromeExtensionURL) { TestHelper("testInvalidChromeExtensionURL", "web_view/shim", NO_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestEventName) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestEventName) { content::ScopedAllowRendererCrashes scoped_allow_renderer_crashes; TestHelper("testEventName", "web_view/shim", NO_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestOnEventProperty) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestOnEventProperty) { TestHelper("testOnEventProperties", "web_view/shim", NO_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestLoadProgressEvent) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestLoadProgressEvent) { TestHelper("testLoadProgressEvent", "web_view/shim", NO_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestDestroyOnEventListener) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestDestroyOnEventListener) { TestHelper("testDestroyOnEventListener", "web_view/shim", NO_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestCannotMutateEventName) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestCannotMutateEventName) { TestHelper("testCannotMutateEventName", "web_view/shim", NO_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestPartitionChangeAfterNavigation) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestPartitionChangeAfterNavigation) { TestHelper("testPartitionChangeAfterNavigation", "web_view/shim", NO_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestPartitionRemovalAfterNavigationFails) { TestHelper("testPartitionRemovalAfterNavigationFails", "web_view/shim", NO_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestAddContentScript) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestAddContentScript) { TestHelper("testAddContentScript", "web_view/shim", NEEDS_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestAddMultipleContentScripts) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestAddMultipleContentScripts) { TestHelper("testAddMultipleContentScripts", "web_view/shim", NEEDS_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P( +IN_PROC_BROWSER_TEST_F( WebViewTest, Shim_TestAddContentScriptWithSameNameShouldOverwriteTheExistingOne) { TestHelper("testAddContentScriptWithSameNameShouldOverwriteTheExistingOne", "web_view/shim", NEEDS_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P( +IN_PROC_BROWSER_TEST_F( WebViewTest, Shim_TestAddContentScriptToOneWebViewShouldNotInjectToTheOtherWebView) { TestHelper("testAddContentScriptToOneWebViewShouldNotInjectToTheOtherWebView", "web_view/shim", NEEDS_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestAddAndRemoveContentScripts) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestAddAndRemoveContentScripts) { TestHelper("testAddAndRemoveContentScripts", "web_view/shim", NEEDS_TEST_SERVER); } @@ -1550,7 +1474,7 @@ #endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) } -IN_PROC_BROWSER_TEST_P( +IN_PROC_BROWSER_TEST_F( WebViewTest, Shim_TestContentScriptIsInjectedAfterTerminateAndReloadWebView) { content::ScopedAllowRendererCrashes scoped_allow_renderer_crashes; @@ -1558,34 +1482,34 @@ "web_view/shim", NEEDS_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestContentScriptExistsAsLongAsWebViewTagExists) { TestHelper("testContentScriptExistsAsLongAsWebViewTagExists", "web_view/shim", NEEDS_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestAddContentScriptWithCode) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestAddContentScriptWithCode) { TestHelper("testAddContentScriptWithCode", "web_view/shim", NEEDS_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P( +IN_PROC_BROWSER_TEST_F( WebViewTest, Shim_TestAddMultipleContentScriptsWithCodeAndCheckGeneratedScriptUrl) { TestHelper("testAddMultipleContentScriptsWithCodeAndCheckGeneratedScriptUrl", "web_view/shim", NEEDS_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestExecuteScriptFail) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestExecuteScriptFail) { TestHelper("testExecuteScriptFail", "web_view/shim", NEEDS_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestExecuteScript) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestExecuteScript) { TestHelper("testExecuteScript", "web_view/shim", NO_TEST_SERVER); } // Flaky and likely not testing the right assertion. https://crbug.com/703727 -IN_PROC_BROWSER_TEST_P( +IN_PROC_BROWSER_TEST_F( WebViewTest, DISABLED_Shim_TestExecuteScriptIsAbortedWhenWebViewSourceIsChanged) { TestHelper("testExecuteScriptIsAbortedWhenWebViewSourceIsChanged", @@ -1593,7 +1517,7 @@ NO_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P( +IN_PROC_BROWSER_TEST_F( WebViewTest, Shim_TestExecuteScriptIsAbortedWhenWebViewSourceIsInvalid) { TestHelper("testExecuteScriptIsAbortedWhenWebViewSourceIsInvalid", @@ -1601,32 +1525,32 @@ NO_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestTerminateAfterExit) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestTerminateAfterExit) { content::ScopedAllowRendererCrashes scoped_allow_renderer_crashes; TestHelper("testTerminateAfterExit", "web_view/shim", NO_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestAssignSrcAfterCrash) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestAssignSrcAfterCrash) { content::ScopedAllowRendererCrashes scoped_allow_renderer_crashes; TestHelper("testAssignSrcAfterCrash", "web_view/shim", NO_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestNavOnConsecutiveSrcAttributeChanges) { TestHelper("testNavOnConsecutiveSrcAttributeChanges", "web_view/shim", NO_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestNavOnSrcAttributeChange) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestNavOnSrcAttributeChange) { TestHelper("testNavOnSrcAttributeChange", "web_view/shim", NO_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestNavigateAfterResize) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestNavigateAfterResize) { TestHelper("testNavigateAfterResize", "web_view/shim", NO_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestNestedCrossOriginSubframes) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestNestedCrossOriginSubframes) { TestHelper("testNestedCrossOriginSubframes", "web_view/shim", NEEDS_TEST_SERVER); } @@ -1637,15 +1561,15 @@ #else #define MAYBE_Shim_TestNestedSubframes Shim_TestNestedSubframes #endif -IN_PROC_BROWSER_TEST_P(WebViewTest, MAYBE_Shim_TestNestedSubframes) { +IN_PROC_BROWSER_TEST_F(WebViewTest, MAYBE_Shim_TestNestedSubframes) { TestHelper("testNestedSubframes", "web_view/shim", NO_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestRemoveSrcAttribute) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestRemoveSrcAttribute) { TestHelper("testRemoveSrcAttribute", "web_view/shim", NO_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestReassignSrcAttribute) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestReassignSrcAttribute) { TestHelper("testReassignSrcAttribute", "web_view/shim", NO_TEST_SERVER); } @@ -1708,15 +1632,8 @@ EXPECT_EQ(guest_instance1->GetStoragePartitionConfig(), guest_instance2->GetStoragePartitionConfig()); - // Without <webview> site isolation, both guests should be in the same - // SiteInstance, even in this `opener_suppressed` case which typically places - // the new window in a new BrowsingInstance. With site isolation, the new - // guest should be in a different BrowsingInstance. - if (content::SiteIsolationPolicy::IsSiteIsolationForGuestsEnabled()) { - EXPECT_FALSE(guest_instance1->IsRelatedSiteInstance(guest_instance2)); - } else { - EXPECT_EQ(guest_instance1, guest_instance2); - } + // The new guest should be in a different BrowsingInstance. + EXPECT_FALSE(guest_instance1->IsRelatedSiteInstance(guest_instance2)); // Check that the source SiteInstance used when the first guest opened the // new noreferrer window is also a guest SiteInstance in the same @@ -1800,7 +1717,7 @@ // with two iframes and a webview within each of the iframes. The // purpose of the test is to ensure that webRequest subevent names are // unique across all webviews within the app. -IN_PROC_BROWSER_TEST_P(WebViewTest, TwoIframesWebRequest) { +IN_PROC_BROWSER_TEST_F(WebViewTest, TwoIframesWebRequest) { ASSERT_TRUE(StartEmbeddedTestServer()); // For serving webview pages. ExtensionTestMessageListener ready1("ready1", ReplyBehavior::kWillReply); ExtensionTestMessageListener ready2("ready2", ReplyBehavior::kWillReply); @@ -2005,121 +1922,121 @@ unattached_guest->web_contents()->GetResponsibleWebContents()); } -IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestContentLoadEvent) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestContentLoadEvent) { TestHelper("testContentLoadEvent", "web_view/shim", NO_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestContentLoadEventWithDisplayNone) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestContentLoadEventWithDisplayNone) { TestHelper("testContentLoadEventWithDisplayNone", "web_view/shim", NO_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestDeclarativeWebRequestAPI) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestDeclarativeWebRequestAPI) { TestHelper("testDeclarativeWebRequestAPI", "web_view/shim", NEEDS_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestDeclarativeWebRequestAPISendMessage) { TestHelper("testDeclarativeWebRequestAPISendMessage", "web_view/shim", NEEDS_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P( +IN_PROC_BROWSER_TEST_F( WebViewTest, Shim_TestDeclarativeWebRequestAPISendMessageSecondWebView) { TestHelper("testDeclarativeWebRequestAPISendMessageSecondWebView", "web_view/shim", NEEDS_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestWebRequestAPI) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestWebRequestAPI) { TestHelper("testWebRequestAPI", "web_view/shim", NEEDS_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestWebRequestAPIOnlyForInstance) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestWebRequestAPIOnlyForInstance) { TestHelper("testWebRequestAPIOnlyForInstance", "web_view/shim", NEEDS_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestWebRequestAPIWithHeaders) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestWebRequestAPIWithHeaders) { TestHelper("testWebRequestAPIWithHeaders", "web_view/shim", NEEDS_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestWebRequestAPIGoogleProperty) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestWebRequestAPIGoogleProperty) { TestHelper("testWebRequestAPIGoogleProperty", "web_view/shim", NO_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestWebRequestListenerSurvivesReparenting) { TestHelper("testWebRequestListenerSurvivesReparenting", "web_view/shim", NEEDS_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestLoadStartLoadRedirect) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestLoadStartLoadRedirect) { TestHelper("testLoadStartLoadRedirect", "web_view/shim", NEEDS_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestLoadAbortChromeExtensionURLWrongPartition) { TestHelper("testLoadAbortChromeExtensionURLWrongPartition", "web_view/shim", NO_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestLoadAbortEmptyResponse) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestLoadAbortEmptyResponse) { TestHelper("testLoadAbortEmptyResponse", "web_view/shim", NEEDS_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestLoadAbortIllegalChromeURL) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestLoadAbortIllegalChromeURL) { TestHelper("testLoadAbortIllegalChromeURL", "web_view/shim", NO_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestLoadAbortIllegalFileURL) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestLoadAbortIllegalFileURL) { TestHelper("testLoadAbortIllegalFileURL", "web_view/shim", NO_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestLoadAbortIllegalJavaScriptURL) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestLoadAbortIllegalJavaScriptURL) { TestHelper("testLoadAbortIllegalJavaScriptURL", "web_view/shim", NO_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestLoadAbortInvalidNavigation) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestLoadAbortInvalidNavigation) { TestHelper("testLoadAbortInvalidNavigation", "web_view/shim", NO_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestLoadAbortNonWebSafeScheme) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestLoadAbortNonWebSafeScheme) { TestHelper("testLoadAbortNonWebSafeScheme", "web_view/shim", NO_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestReload) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestReload) { TestHelper("testReload", "web_view/shim", NO_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestReloadAfterTerminate) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestReloadAfterTerminate) { content::ScopedAllowRendererCrashes scoped_allow_renderer_crashes; TestHelper("testReloadAfterTerminate", "web_view/shim", NO_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestGetProcessId) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestGetProcessId) { TestHelper("testGetProcessId", "web_view/shim", NO_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewVisibilityTest, Shim_TestHiddenBeforeNavigation) { +IN_PROC_BROWSER_TEST_F(WebViewVisibilityTest, Shim_TestHiddenBeforeNavigation) { TestHelper("testHiddenBeforeNavigation", "web_view/shim", NO_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestRemoveWebviewOnExit) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestRemoveWebviewOnExit) { content::ScopedAllowRendererCrashes scoped_allow_renderer_crashes; ASSERT_TRUE(StartEmbeddedTestServer()); // For serving guest pages. @@ -2171,67 +2088,50 @@ // Remove <webview> immediately after navigating it. // This is a regression test for http://crbug.com/276023. -IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestRemoveWebviewAfterNavigation) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestRemoveWebviewAfterNavigation) { TestHelper("testRemoveWebviewAfterNavigation", "web_view/shim", NO_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestNavigationToExternalProtocol) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestNavigationToExternalProtocol) { TestHelper("testNavigationToExternalProtocol", "web_view/shim", NO_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewSizeTest, +IN_PROC_BROWSER_TEST_F(WebViewSizeTest, Shim_TestResizeWebviewWithDisplayNoneResizesContent) { TestHelper("testResizeWebviewWithDisplayNoneResizesContent", "web_view/shim", NO_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewSizeTest, Shim_TestResizeWebviewResizesContent) { +IN_PROC_BROWSER_TEST_F(WebViewSizeTest, Shim_TestResizeWebviewResizesContent) { TestHelper("testResizeWebviewResizesContent", "web_view/shim", NO_TEST_SERVER); } -class WebViewSSLErrorTest - : public WebViewTestBase, - public testing::WithParamInterface<testing::tuple<bool, bool>> { +class WebViewSSLErrorTest : public WebViewTest, + public testing::WithParamInterface<bool> { public: WebViewSSLErrorTest() { - auto [is_site_isolation_enabled, use_interstitials] = GetParam(); - std::vector<base::test::FeatureRef> enabled_features, disabled_features; - if (is_site_isolation_enabled) { - enabled_features.push_back(features::kSiteIsolationForGuests); - } else { - disabled_features.push_back(features::kSiteIsolationForGuests); - } - - if (use_interstitials) { - disabled_features.push_back( - extensions_features::kWebviewTagMPArchBehavior); - } else { - enabled_features.push_back( - extensions_features::kWebviewTagMPArchBehavior); - } - - scoped_feature_list_.InitWithFeatures(std::move(enabled_features), - std::move(disabled_features)); + bool use_interstitials = GetParam(); + scoped_feature_list_.InitWithFeatureState( + extensions_features::kWebviewTagMPArchBehavior, + /*enabled=*/!use_interstitials); } ~WebViewSSLErrorTest() override = default; static std::string DescribeParams( const testing::TestParamInfo<ParamType>& info) { - auto [is_site_isolation_enabled, use_interstitials] = info.param; - return base::StringPrintf( - "SiteIsolationForGuests%s_Use%s", - is_site_isolation_enabled ? "Enabled" : "Disabled", - use_interstitials ? "Interstitial" : "ErrorPage"); + bool use_interstitials = info.param; + return base::StringPrintf("Use%s", + use_interstitials ? "Interstitial" : "ErrorPage"); } - bool UseInterstitials() { return testing::get<1>(GetParam()); } + bool UseInterstitials() { return GetParam(); } // Loads the guest at "web_view/ssl/https_page.html" with an SSL error, and // asserts the security interstitial is not displayed for guest through the @@ -2317,7 +2217,7 @@ INSTANTIATE_TEST_SUITE_P(WebViewSSLErrorTests, WebViewSSLErrorTest, - testing::Combine(testing::Bool(), testing::Bool()), + testing::Bool(), WebViewSSLErrorTest::DescribeParams); // Test makes sure that an error document is shown in `<webview>` with an SSL @@ -2449,12 +2349,7 @@ safe_browsing_factory_; }; -INSTANTIATE_TEST_SUITE_P(WebViewTests, - WebViewSafeBrowsingTest, - testing::Bool(), - WebViewTest::DescribeParams); - -IN_PROC_BROWSER_TEST_P(WebViewSafeBrowsingTest, +IN_PROC_BROWSER_TEST_F(WebViewSafeBrowsingTest, Shim_TestLoadAbortSafeBrowsing) { // We start the test server here, instead of in TestHelper, because we need // to know the URL to treat as dangerous before running the rest of the test. @@ -2476,7 +2371,7 @@ INSTANTIATE_TEST_SUITE_P(WebViewHttpsFirstModeTests, WebViewHttpsFirstModeTest, - testing::Combine(testing::Bool(), testing::Bool()), + testing::Bool(), WebViewHttpsFirstModeTest::DescribeParams); // Tests that loading an HTTPS page in a guest <webview> with HTTPS-First Mode @@ -2528,7 +2423,7 @@ ASSERT_FALSE(IsShowingInterstitial(GetFirstAppWindowWebContents())); } -IN_PROC_BROWSER_TEST_P(WebViewTest, ShimSrcAttribute) { +IN_PROC_BROWSER_TEST_F(WebViewTest, ShimSrcAttribute) { ASSERT_TRUE(RunExtensionTest("platform_apps/web_view/src_attribute", {.launch_as_platform_app = true})) << message_; @@ -2537,7 +2432,7 @@ // This test verifies that prerendering has been disabled inside <webview>. // This test is here rather than in PrerenderBrowserTest for testing convenience // only. If it breaks then this is a bug in the prerenderer. -IN_PROC_BROWSER_TEST_P(WebViewTest, NoPrerenderer) { +IN_PROC_BROWSER_TEST_F(WebViewTest, NoPrerenderer) { ASSERT_TRUE(StartEmbeddedTestServer()); LoadAndLaunchPlatformApp("web_view/noprerenderer", "guest-loaded"); auto* guest_rfh = @@ -2553,7 +2448,7 @@ // Verify that existing <webview>'s are detected when the task manager starts // up. -IN_PROC_BROWSER_TEST_P(WebViewTest, TaskManagerExistingWebView) { +IN_PROC_BROWSER_TEST_F(WebViewTest, TaskManagerExistingWebView) { ASSERT_TRUE(StartEmbeddedTestServer()); LoadAndLaunchPlatformApp("web_view/task_manager", "guest-loaded"); @@ -2576,7 +2471,7 @@ } // Verify that the task manager notices the creation of new <webview>'s. -IN_PROC_BROWSER_TEST_P(WebViewTest, TaskManagerNewWebView) { +IN_PROC_BROWSER_TEST_F(WebViewTest, TaskManagerNewWebView) { ASSERT_TRUE(StartEmbeddedTestServer()); chrome::ShowTaskManager(browser()); // Show task manager BEFORE guest loads. @@ -2602,7 +2497,7 @@ // the main browser window to a page that sets a cookie and loads an app with // multiple webview tags. Each tag sets a cookie and the test checks the proper // storage isolation is enforced. -IN_PROC_BROWSER_TEST_P(WebViewTest, CookieIsolation) { +IN_PROC_BROWSER_TEST_F(WebViewTest, CookieIsolation) { ASSERT_TRUE(StartEmbeddedTestServer()); // Navigate the browser to a page which writes a sample cookie // The cookie is "testCookie=1" @@ -2628,7 +2523,7 @@ // This tests that in-memory storage partitions are reset on browser restart, // but persistent ones maintain state for cookies and HTML5 storage. -IN_PROC_BROWSER_TEST_P(WebViewTest, PRE_StoragePersistence) { +IN_PROC_BROWSER_TEST_F(WebViewTest, PRE_StoragePersistence) { ASSERT_TRUE(StartEmbeddedTestServer()); // We don't care where the main browser is on this test. ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GURL("about:blank"))); @@ -2641,7 +2536,7 @@ // This is the post-reset portion of the StoragePersistence test. See // PRE_StoragePersistence for main comment. -IN_PROC_BROWSER_TEST_P(WebViewTest, StoragePersistence) { +IN_PROC_BROWSER_TEST_F(WebViewTest, StoragePersistence) { ASSERT_TRUE(StartEmbeddedTestServer()); // We don't care where the main browser is on this test. ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GURL("about:blank"))); @@ -2656,7 +2551,7 @@ // loads an app with multiple webview tags and each tag sets DOM storage // entries, which the test checks to ensure proper storage isolation is // enforced. -IN_PROC_BROWSER_TEST_P(WebViewTest, DOMStorageIsolation) { +IN_PROC_BROWSER_TEST_F(WebViewTest, DOMStorageIsolation) { ASSERT_TRUE(StartEmbeddedTestServer()); GURL navigate_to_url = embedded_test_server()->GetURL( @@ -2693,7 +2588,7 @@ // in the same storage partition stopped being able to find each other). // This is also a regression test for https://crbug.com/802278 (setting of // a guestview as an opener should not leak any memory). -IN_PROC_BROWSER_TEST_P(WebViewTest, FindabilityIsolation) { +IN_PROC_BROWSER_TEST_F(WebViewTest, FindabilityIsolation) { ASSERT_TRUE(StartEmbeddedTestServer()); GURL navigate_to_url = embedded_test_server()->GetURL( @@ -2710,7 +2605,7 @@ // This tests IndexedDB isolation for packaged apps with webview tags. It loads // an app with multiple webview tags and each tag creates an IndexedDB record, // which the test checks to ensure proper storage isolation is enforced. -IN_PROC_BROWSER_TEST_P(WebViewTest, IndexedDBIsolation) { +IN_PROC_BROWSER_TEST_F(WebViewTest, IndexedDBIsolation) { ASSERT_TRUE(StartEmbeddedTestServer()); ASSERT_TRUE(RunExtensionTest("platform_apps/web_view/isolation_indexeddb", {.launch_as_platform_app = true})) @@ -2727,37 +2622,36 @@ #else #define MAYBE_CloseOnLoadcommit CloseOnLoadcommit #endif -IN_PROC_BROWSER_TEST_P(WebViewTest, MAYBE_CloseOnLoadcommit) { +IN_PROC_BROWSER_TEST_F(WebViewTest, MAYBE_CloseOnLoadcommit) { LoadAndLaunchPlatformApp("web_view/close_on_loadcommit", "done-close-on-loadcommit"); } -IN_PROC_BROWSER_TEST_P(WebViewTest, MediaAccessAPIDeny_TestDeny) { +IN_PROC_BROWSER_TEST_F(WebViewTest, MediaAccessAPIDeny_TestDeny) { MediaAccessAPIDenyTestHelper("testDeny"); } -IN_PROC_BROWSER_TEST_P(WebViewTest, +IN_PROC_BROWSER_TEST_F(WebViewTest, MediaAccessAPIDeny_TestDenyThenAllowThrows) { MediaAccessAPIDenyTestHelper("testDenyThenAllowThrows"); } -IN_PROC_BROWSER_TEST_P(WebViewTest, +IN_PROC_BROWSER_TEST_F(WebViewTest, MediaAccessAPIDeny_TestDenyWithPreventDefault) { MediaAccessAPIDenyTestHelper("testDenyWithPreventDefault"); } -IN_PROC_BROWSER_TEST_P(WebViewTest, +IN_PROC_BROWSER_TEST_F(WebViewTest, MediaAccessAPIDeny_TestNoListenersImplyDeny) { MediaAccessAPIDenyTestHelper("testNoListenersImplyDeny"); } -IN_PROC_BROWSER_TEST_P(WebViewTest, +IN_PROC_BROWSER_TEST_F(WebViewTest, MediaAccessAPIDeny_TestNoPreventDefaultImpliesDeny) { MediaAccessAPIDenyTestHelper("testNoPreventDefaultImpliesDeny"); } -void WebViewTestBase::MediaAccessAPIAllowTestHelper( - const std::string& test_name) { +void WebViewTest::MediaAccessAPIAllowTestHelper(const std::string& test_name) { ASSERT_TRUE(StartEmbeddedTestServer()); // For serving guest pages. LoadAndLaunchPlatformApp("web_view/media_access/allow", "Launched"); @@ -2778,7 +2672,7 @@ mock->WaitForRequestMediaPermission(); } -IN_PROC_BROWSER_TEST_P(WebViewTest, OpenURLFromTab_CurrentTab_Abort) { +IN_PROC_BROWSER_TEST_F(WebViewTest, OpenURLFromTab_CurrentTab_Abort) { LoadAppWithGuest("web_view/simple"); // Verify that OpenURLFromTab with a window disposition of CURRENT_TAB will @@ -2802,7 +2696,7 @@ // A navigation to a web-safe URL should succeed, even if it is not renderer- // initiated, such as a navigation from the PDF viewer. -IN_PROC_BROWSER_TEST_P(WebViewTest, OpenURLFromTab_CurrentTab_Succeed) { +IN_PROC_BROWSER_TEST_F(WebViewTest, OpenURLFromTab_CurrentTab_Succeed) { LoadAppWithGuest("web_view/simple"); // Verify that OpenURLFromTab with a window disposition of CURRENT_TAB will @@ -2883,7 +2777,7 @@ EXPECT_TRUE(content::NavigateToURLFromRenderer(guest2, coop_url)); } -IN_PROC_BROWSER_TEST_P(WebViewTest, ContextMenuInspectElement) { +IN_PROC_BROWSER_TEST_F(WebViewTest, ContextMenuInspectElement) { LoadAppWithGuest("web_view/context_menus/basic"); content::RenderFrameHost* guest_rfh = GetGuestRenderFrameHost(); ASSERT_TRUE(guest_rfh); @@ -2900,7 +2794,7 @@ // load chrome://settings/languages in a browser window. This is a browser- // initiated operation and so we expect this to succeed if the embedder is // allowed to perform the operation. -IN_PROC_BROWSER_TEST_P(WebViewTest, ContextMenuLanguageSettings) { +IN_PROC_BROWSER_TEST_F(WebViewTest, ContextMenuLanguageSettings) { LoadAppWithGuest("web_view/context_menus/basic"); content::WebContents* embedder = GetEmbedderWebContents(); @@ -2925,7 +2819,7 @@ new_contents->GetVisibleURL()); } -IN_PROC_BROWSER_TEST_P(WebViewTest, ContextMenusAPI_Basic) { +IN_PROC_BROWSER_TEST_F(WebViewTest, ContextMenusAPI_Basic) { LoadAppWithGuest("web_view/context_menus/basic"); content::WebContents* embedder = GetEmbedderWebContents(); @@ -2976,7 +2870,7 @@ ASSERT_EQ(0u, items_after_all_removal.size()); } -IN_PROC_BROWSER_TEST_P(WebViewTest, ContextMenusAPI_PreventDefault) { +IN_PROC_BROWSER_TEST_F(WebViewTest, ContextMenusAPI_PreventDefault) { LoadAppWithGuest("web_view/context_menus/basic"); auto* guest_main_frame = GetGuestViewManager()->GetLastGuestRenderFrameHostCreated(); @@ -3009,7 +2903,7 @@ // Tests that a context menu is created when right-clicking in the webview. This // also tests that the 'contextmenu' event is handled correctly. -IN_PROC_BROWSER_TEST_P(WebViewTest, TestContextMenu) { +IN_PROC_BROWSER_TEST_F(WebViewTest, TestContextMenu) { LoadAppWithGuest("web_view/context_menus/basic"); auto* guest_main_frame = GetGuestViewManager()->WaitForSingleGuestRenderFrameHostCreated(); @@ -3034,23 +2928,23 @@ run_loop.Run(); } -IN_PROC_BROWSER_TEST_P(WebViewTest, MediaAccessAPIAllow_TestAllow) { +IN_PROC_BROWSER_TEST_F(WebViewTest, MediaAccessAPIAllow_TestAllow) { MediaAccessAPIAllowTestHelper("testAllow"); } -IN_PROC_BROWSER_TEST_P(WebViewTest, MediaAccessAPIAllow_TestAllowAndThenDeny) { +IN_PROC_BROWSER_TEST_F(WebViewTest, MediaAccessAPIAllow_TestAllowAndThenDeny) { MediaAccessAPIAllowTestHelper("testAllowAndThenDeny"); } -IN_PROC_BROWSER_TEST_P(WebViewTest, MediaAccessAPIAllow_TestAllowTwice) { +IN_PROC_BROWSER_TEST_F(WebViewTest, MediaAccessAPIAllow_TestAllowTwice) { MediaAccessAPIAllowTestHelper("testAllowTwice"); } -IN_PROC_BROWSER_TEST_P(WebViewTest, MediaAccessAPIAllow_TestAllowAsync) { +IN_PROC_BROWSER_TEST_F(WebViewTest, MediaAccessAPIAllow_TestAllowAsync) { MediaAccessAPIAllowTestHelper("testAllowAsync"); } -IN_PROC_BROWSER_TEST_P(WebViewTest, MediaAccessAPIAllow_TestCheck) { +IN_PROC_BROWSER_TEST_F(WebViewTest, MediaAccessAPIAllow_TestCheck) { ASSERT_TRUE(StartEmbeddedTestServer()); // For serving guest pages. LoadAndLaunchPlatformApp("web_view/media_access/check", "Launched"); @@ -3072,7 +2966,7 @@ // Checks that window.screenX/screenY/screenLeft/screenTop works correctly for // guests. -IN_PROC_BROWSER_TEST_P(WebViewTest, ScreenCoordinates) { +IN_PROC_BROWSER_TEST_F(WebViewTest, ScreenCoordinates) { ASSERT_TRUE(RunExtensionTest( "platform_apps/web_view/common", {.custom_arg = "screen_coordinates", .launch_as_platform_app = true})) @@ -3085,7 +2979,7 @@ #else #define MAYBE_TearDownTest TearDownTest #endif -IN_PROC_BROWSER_TEST_P(WebViewTest, MAYBE_TearDownTest) { +IN_PROC_BROWSER_TEST_F(WebViewTest, MAYBE_TearDownTest) { const extensions::Extension* extension = LoadAndLaunchPlatformApp("web_view/simple", "WebViewTest.LAUNCHED"); extensions::AppWindow* window = nullptr; @@ -3101,7 +2995,7 @@ // Tests that an app can inject a content script into a webview, and that it can // send cross-origin requests with CORS headers. -IN_PROC_BROWSER_TEST_P(WebViewTest, ContentScriptFetch) { +IN_PROC_BROWSER_TEST_F(WebViewTest, ContentScriptFetch) { TestHelper("testContentScriptFetch", "web_view/content_script_fetch", NEEDS_TEST_SERVER); } @@ -3110,13 +3004,13 @@ // platform app) does not have geolocation permission for this test. // No matter what the API does, geolocation permission would be denied. // Note that the test name prefix must be "GeolocationAPI". -IN_PROC_BROWSER_TEST_P(WebViewTest, GeolocationAPIEmbedderHasNoAccessAllow) { +IN_PROC_BROWSER_TEST_F(WebViewTest, GeolocationAPIEmbedderHasNoAccessAllow) { TestHelper("testDenyDenies", "web_view/geolocation/embedder_has_no_permission", NEEDS_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, GeolocationAPIEmbedderHasNoAccessDeny) { +IN_PROC_BROWSER_TEST_F(WebViewTest, GeolocationAPIEmbedderHasNoAccessDeny) { TestHelper("testDenyDenies", "web_view/geolocation/embedder_has_no_permission", NEEDS_TEST_SERVER); @@ -3130,14 +3024,14 @@ // the tests become flaky. // // GeolocationAPI* test 1 of 3. -IN_PROC_BROWSER_TEST_P(WebViewTest, GeolocationAPIEmbedderHasAccessAllow) { +IN_PROC_BROWSER_TEST_F(WebViewTest, GeolocationAPIEmbedderHasAccessAllow) { TestHelper("testAllow", "web_view/geolocation/embedder_has_permission", NEEDS_TEST_SERVER); } // GeolocationAPI* test 2 of 3. -IN_PROC_BROWSER_TEST_P(WebViewTest, GeolocationAPIEmbedderHasAccessDeny) { +IN_PROC_BROWSER_TEST_F(WebViewTest, GeolocationAPIEmbedderHasAccessDeny) { TestHelper("testDeny", "web_view/geolocation/embedder_has_permission", NEEDS_TEST_SERVER); @@ -3145,115 +3039,115 @@ // GeolocationAPI* test 3 of 3. // Currently disabled until crbug.com/526788 is fixed. -IN_PROC_BROWSER_TEST_P(WebViewTest, +IN_PROC_BROWSER_TEST_F(WebViewTest, GeolocationAPIEmbedderHasAccessMultipleBridgeIdAllow) { TestHelper("testMultipleBridgeIdAllow", "web_view/geolocation/embedder_has_permission", NEEDS_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, +IN_PROC_BROWSER_TEST_F(WebViewTest, PermissionsAPIEmbedderHasAccessAllowGeolocation) { TestHelper("testAllowGeolocation", "web_view/permissions_test/embedder_has_permission", NEEDS_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, +IN_PROC_BROWSER_TEST_F(WebViewTest, PermissionsAPIEmbedderHasAccessDenyGeolocation) { TestHelper("testDenyGeolocation", "web_view/permissions_test/embedder_has_permission", NEEDS_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, +IN_PROC_BROWSER_TEST_F(WebViewTest, PermissionsAPIEmbedderHasAccessAllowCamera) { TestHelper("testAllowCamera", "web_view/permissions_test/embedder_has_permission", NEEDS_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, PermissionsAPIEmbedderHasAccessDenyCamera) { +IN_PROC_BROWSER_TEST_F(WebViewTest, PermissionsAPIEmbedderHasAccessDenyCamera) { TestHelper("testDenyCamera", "web_view/permissions_test/embedder_has_permission", NEEDS_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, +IN_PROC_BROWSER_TEST_F(WebViewTest, PermissionsAPIEmbedderHasAccessAllowMicrophone) { TestHelper("testAllowMicrophone", "web_view/permissions_test/embedder_has_permission", NEEDS_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, +IN_PROC_BROWSER_TEST_F(WebViewTest, PermissionsAPIEmbedderHasAccessDenyMicrophone) { TestHelper("testDenyMicrophone", "web_view/permissions_test/embedder_has_permission", NEEDS_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, PermissionsAPIEmbedderHasAccessAllowMedia) { +IN_PROC_BROWSER_TEST_F(WebViewTest, PermissionsAPIEmbedderHasAccessAllowMedia) { TestHelper("testAllowMedia", "web_view/permissions_test/embedder_has_permission", NEEDS_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, PermissionsAPIEmbedderHasAccessDenyMedia) { +IN_PROC_BROWSER_TEST_F(WebViewTest, PermissionsAPIEmbedderHasAccessDenyMedia) { TestHelper("testDenyMedia", "web_view/permissions_test/embedder_has_permission", NEEDS_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, +IN_PROC_BROWSER_TEST_F(WebViewTest, PermissionsAPIEmbedderHasNoAccessAllowGeolocation) { TestHelper("testAllowGeolocation", "web_view/permissions_test/embedder_has_no_permission", NEEDS_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, +IN_PROC_BROWSER_TEST_F(WebViewTest, PermissionsAPIEmbedderHasNoAccessDenyGeolocation) { TestHelper("testDenyGeolocation", "web_view/permissions_test/embedder_has_no_permission", NEEDS_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, +IN_PROC_BROWSER_TEST_F(WebViewTest, PermissionsAPIEmbedderHasNoAccessAllowCamera) { TestHelper("testAllowCamera", "web_view/permissions_test/embedder_has_no_permission", NEEDS_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, +IN_PROC_BROWSER_TEST_F(WebViewTest, PermissionsAPIEmbedderHasNoAccessDenyCamera) { TestHelper("testDenyCamera", "web_view/permissions_test/embedder_has_no_permission", NEEDS_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, +IN_PROC_BROWSER_TEST_F(WebViewTest, PermissionsAPIEmbedderHasNoAccessAllowMicrophone) { TestHelper("testAllowMicrophone", "web_view/permissions_test/embedder_has_no_permission", NEEDS_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, +IN_PROC_BROWSER_TEST_F(WebViewTest, PermissionsAPIEmbedderHasNoAccessDenyMicrophone) { TestHelper("testDenyMicrophone", "web_view/permissions_test/embedder_has_no_permission", NEEDS_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, +IN_PROC_BROWSER_TEST_F(WebViewTest, PermissionsAPIEmbedderHasNoAccessAllowMedia) { TestHelper("testAllowMedia", "web_view/permissions_test/embedder_has_no_permission", NEEDS_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, +IN_PROC_BROWSER_TEST_F(WebViewTest, PermissionsAPIEmbedderHasNoAccessDenyMedia) { TestHelper("testDenyMedia", "web_view/permissions_test/embedder_has_no_permission", @@ -3263,7 +3157,7 @@ // Tests that // BrowserPluginGeolocationPermissionContext::CancelGeolocationPermissionRequest // is handled correctly (and does not crash). -IN_PROC_BROWSER_TEST_P(WebViewTest, GeolocationAPICancelGeolocation) { +IN_PROC_BROWSER_TEST_F(WebViewTest, GeolocationAPICancelGeolocation) { ASSERT_TRUE(StartEmbeddedTestServer()); // For serving guest pages. ASSERT_TRUE( RunExtensionTest("platform_apps/web_view/geolocation/cancel_request", @@ -3271,7 +3165,7 @@ << message_; } -IN_PROC_BROWSER_TEST_P(WebViewTest, DISABLED_GeolocationRequestGone) { +IN_PROC_BROWSER_TEST_F(WebViewTest, DISABLED_GeolocationRequestGone) { ASSERT_TRUE(StartEmbeddedTestServer()); // For serving guest pages. ASSERT_TRUE(RunExtensionTest( "platform_apps/web_view/geolocation/geolocation_request_gone", @@ -3282,17 +3176,17 @@ // In following FilesystemAPIRequestFromMainThread* tests, guest request // filesystem access from main thread of the guest. // FileSystemAPIRequestFromMainThread* test 1 of 3 -IN_PROC_BROWSER_TEST_P(WebViewTest, FileSystemAPIRequestFromMainThreadAllow) { +IN_PROC_BROWSER_TEST_F(WebViewTest, FileSystemAPIRequestFromMainThreadAllow) { TestHelper("testAllow", "web_view/filesystem/main", NEEDS_TEST_SERVER); } // FileSystemAPIRequestFromMainThread* test 2 of 3. -IN_PROC_BROWSER_TEST_P(WebViewTest, FileSystemAPIRequestFromMainThreadDeny) { +IN_PROC_BROWSER_TEST_F(WebViewTest, FileSystemAPIRequestFromMainThreadDeny) { TestHelper("testDeny", "web_view/filesystem/main", NEEDS_TEST_SERVER); } // FileSystemAPIRequestFromMainThread* test 3 of 3. -IN_PROC_BROWSER_TEST_P(WebViewTest, +IN_PROC_BROWSER_TEST_F(WebViewTest, FileSystemAPIRequestFromMainThreadDefaultAllow) { TestHelper("testDefaultAllow", "web_view/filesystem/main", NEEDS_TEST_SERVER); } @@ -3300,17 +3194,17 @@ // In following FilesystemAPIRequestFromWorker* tests, guest create a worker // to request filesystem access from worker thread. // FileSystemAPIRequestFromWorker* test 1 of 3 -IN_PROC_BROWSER_TEST_P(WebViewTest, FileSystemAPIRequestFromWorkerAllow) { +IN_PROC_BROWSER_TEST_F(WebViewTest, FileSystemAPIRequestFromWorkerAllow) { TestHelper("testAllow", "web_view/filesystem/worker", NEEDS_TEST_SERVER); } // FileSystemAPIRequestFromWorker* test 2 of 3. -IN_PROC_BROWSER_TEST_P(WebViewTest, FileSystemAPIRequestFromWorkerDeny) { +IN_PROC_BROWSER_TEST_F(WebViewTest, FileSystemAPIRequestFromWorkerDeny) { TestHelper("testDeny", "web_view/filesystem/worker", NEEDS_TEST_SERVER); } // FileSystemAPIRequestFromWorker* test 3 of 3. -IN_PROC_BROWSER_TEST_P(WebViewTest, +IN_PROC_BROWSER_TEST_F(WebViewTest, FileSystemAPIRequestFromWorkerDefaultAllow) { TestHelper( "testDefaultAllow", "web_view/filesystem/worker", NEEDS_TEST_SERVER); @@ -3320,7 +3214,7 @@ // embedder contains a single webview guest. The guest creates a shared worker // to request filesystem access from worker thread. // FileSystemAPIRequestFromSharedWorkerOfSingleWebViewGuest* test 1 of 3 -IN_PROC_BROWSER_TEST_P( +IN_PROC_BROWSER_TEST_F( WebViewTest, FileSystemAPIRequestFromSharedWorkerOfSingleWebViewGuestAllow) { TestHelper("testAllow", @@ -3329,7 +3223,7 @@ } // FileSystemAPIRequestFromSharedWorkerOfSingleWebViewGuest* test 2 of 3. -IN_PROC_BROWSER_TEST_P( +IN_PROC_BROWSER_TEST_F( WebViewTest, FileSystemAPIRequestFromSharedWorkerOfSingleWebViewGuestDeny) { TestHelper("testDeny", @@ -3338,7 +3232,7 @@ } // FileSystemAPIRequestFromSharedWorkerOfSingleWebViewGuest* test 3 of 3. -IN_PROC_BROWSER_TEST_P( +IN_PROC_BROWSER_TEST_F( WebViewTest, FileSystemAPIRequestFromSharedWorkerOfSingleWebViewGuestDefaultAllow) { TestHelper( @@ -3351,7 +3245,7 @@ // embedder contains mutiple webview guests. Each guest creates a shared worker // to request filesystem access from worker thread. // FileSystemAPIRequestFromSharedWorkerOfMultiWebViewGuests* test 1 of 3 -IN_PROC_BROWSER_TEST_P( +IN_PROC_BROWSER_TEST_F( WebViewTest, FileSystemAPIRequestFromSharedWorkerOfMultiWebViewGuestsAllow) { TestHelper("testAllow", @@ -3360,7 +3254,7 @@ } // FileSystemAPIRequestFromSharedWorkerOfMultiWebViewGuests* test 2 of 3. -IN_PROC_BROWSER_TEST_P( +IN_PROC_BROWSER_TEST_F( WebViewTest, FileSystemAPIRequestFromSharedWorkerOfMultiWebViewGuestsDeny) { TestHelper("testDeny", @@ -3369,7 +3263,7 @@ } // FileSystemAPIRequestFromSharedWorkerOfMultiWebViewGuests* test 3 of 3. -IN_PROC_BROWSER_TEST_P( +IN_PROC_BROWSER_TEST_F( WebViewTest, FileSystemAPIRequestFromSharedWorkerOfMultiWebViewGuestsDefaultAllow) { TestHelper( @@ -3378,7 +3272,7 @@ NEEDS_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, ClearData) { +IN_PROC_BROWSER_TEST_F(WebViewTest, ClearData) { ASSERT_TRUE(StartEmbeddedTestServer()); // For serving guest pages. ASSERT_TRUE(RunExtensionTest( "platform_apps/web_view/common", @@ -3386,7 +3280,7 @@ << message_; } -IN_PROC_BROWSER_TEST_P(WebViewTest, ClearSessionCookies) { +IN_PROC_BROWSER_TEST_F(WebViewTest, ClearSessionCookies) { ASSERT_TRUE(StartEmbeddedTestServer()); // For serving guest pages. ASSERT_TRUE(RunExtensionTest( "platform_apps/web_view/common", @@ -3394,7 +3288,7 @@ << message_; } -IN_PROC_BROWSER_TEST_P(WebViewTest, ClearPersistentCookies) { +IN_PROC_BROWSER_TEST_F(WebViewTest, ClearPersistentCookies) { ASSERT_TRUE(StartEmbeddedTestServer()); // For serving guest pages. ASSERT_TRUE(RunExtensionTest( "platform_apps/web_view/common", @@ -3403,7 +3297,7 @@ } // Regression test for https://crbug.com/615429. -IN_PROC_BROWSER_TEST_P(WebViewTest, ClearDataTwice) { +IN_PROC_BROWSER_TEST_F(WebViewTest, ClearDataTwice) { ASSERT_TRUE(StartEmbeddedTestServer()); // For serving guest pages. ASSERT_TRUE(RunExtensionTest( "platform_apps/web_view/common", @@ -3418,18 +3312,18 @@ #else #define MAYBE_ClearDataCache ClearDataCache #endif -IN_PROC_BROWSER_TEST_P(WebViewTest, MAYBE_ClearDataCache) { +IN_PROC_BROWSER_TEST_F(WebViewTest, MAYBE_ClearDataCache) { TestHelper("testClearCache", "web_view/clear_data_cache", NEEDS_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, ConsoleMessage) { +IN_PROC_BROWSER_TEST_F(WebViewTest, ConsoleMessage) { ASSERT_TRUE(RunExtensionTest( "platform_apps/web_view/common", {.custom_arg = "console_messages", .launch_as_platform_app = true})) << message_; } -IN_PROC_BROWSER_TEST_P(WebViewTest, DownloadPermission) { +IN_PROC_BROWSER_TEST_F(WebViewTest, DownloadPermission) { ASSERT_TRUE(StartEmbeddedTestServer()); // For serving guest pages. LoadAndLaunchPlatformApp("web_view/download", "guest-loaded"); auto* guest_view_base = @@ -3541,7 +3435,7 @@ // Downloads initiated from isolated guest parititons should use their // respective cookie stores. In addition, if those downloads are resumed, they // should continue to use their respective cookie stores. -IN_PROC_BROWSER_TEST_P(WebViewTest, DownloadCookieIsolation) { +IN_PROC_BROWSER_TEST_F(WebViewTest, DownloadCookieIsolation) { embedded_test_server()->RegisterRequestHandler( base::BindRepeating(&HandleDownloadRequestWithCookie)); ASSERT_TRUE(StartEmbeddedTestServer()); // For serving guest pages. @@ -3627,7 +3521,7 @@ ASSERT_TRUE(cookies.find("cookie=second") != cookies.end()); } -IN_PROC_BROWSER_TEST_P(WebViewTest, PRE_DownloadCookieIsolation_CrossSession) { +IN_PROC_BROWSER_TEST_F(WebViewTest, PRE_DownloadCookieIsolation_CrossSession) { embedded_test_server()->RegisterRequestHandler( base::BindRepeating(&HandleDownloadRequestWithCookie)); ASSERT_TRUE(StartEmbeddedTestServer()); // For serving guest pages. @@ -3683,7 +3577,7 @@ content::EnsureCookiesFlushed(profile()); } -IN_PROC_BROWSER_TEST_P(WebViewTest, DownloadCookieIsolation_CrossSession) { +IN_PROC_BROWSER_TEST_F(WebViewTest, DownloadCookieIsolation_CrossSession) { embedded_test_server()->RegisterRequestHandler( base::BindRepeating(&HandleDownloadRequestWithCookie)); ASSERT_TRUE(StartEmbeddedTestServer()); // For serving guest pages. @@ -3777,7 +3671,7 @@ // This test makes sure loading <webview> does not crash when there is an // extension which has content script allowlisted/forced. -IN_PROC_BROWSER_TEST_P(WebViewTest, AllowlistedContentScript) { +IN_PROC_BROWSER_TEST_F(WebViewTest, AllowlistedContentScript) { // Allowlist the extension for running content script we are going to load. extensions::ExtensionsClient::ScriptingAllowlist allowlist; const std::string extension_id = "imeongpbjoodlnmlakaldhlcmijmhpbb"; @@ -3796,7 +3690,7 @@ "TEST_PASSED"); } -IN_PROC_BROWSER_TEST_P(WebViewTest, SendMessageToExtensionFromGuest) { +IN_PROC_BROWSER_TEST_F(WebViewTest, SendMessageToExtensionFromGuest) { // Load the extension as a normal, non-component extension. const extensions::Extension* extension = LoadExtension(test_data_dir_.AppendASCII( @@ -3807,7 +3701,7 @@ NEEDS_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, SendMessageToComponentExtensionFromGuest) { +IN_PROC_BROWSER_TEST_F(WebViewTest, SendMessageToComponentExtensionFromGuest) { const extensions::Extension* component_extension = LoadExtensionAsComponent(test_data_dir_.AppendASCII( "platform_apps/web_view/extension_api/component_extension")); @@ -3839,19 +3733,19 @@ content::WebContents::FromRenderFrameHost(guest_rfh))); } -IN_PROC_BROWSER_TEST_P(WebViewTest, SetPropertyOnDocumentReady) { +IN_PROC_BROWSER_TEST_F(WebViewTest, SetPropertyOnDocumentReady) { ASSERT_TRUE(RunExtensionTest("platform_apps/web_view/document_ready", {.launch_as_platform_app = true})) << message_; } -IN_PROC_BROWSER_TEST_P(WebViewTest, SetPropertyOnDocumentInteractive) { +IN_PROC_BROWSER_TEST_F(WebViewTest, SetPropertyOnDocumentInteractive) { ASSERT_TRUE(RunExtensionTest("platform_apps/web_view/document_interactive", {.launch_as_platform_app = true})) << message_; } -IN_PROC_BROWSER_TEST_P(WebViewSpeechAPITest, +IN_PROC_BROWSER_TEST_F(WebViewSpeechAPITest, SpeechRecognitionAPI_HasPermissionAllow) { ASSERT_TRUE(RunExtensionTest( "platform_apps/web_view/speech_recognition_api", @@ -3859,7 +3753,7 @@ << message_; } -IN_PROC_BROWSER_TEST_P(WebViewSpeechAPITest, +IN_PROC_BROWSER_TEST_F(WebViewSpeechAPITest, SpeechRecognitionAPI_HasPermissionDeny) { ASSERT_TRUE(RunExtensionTest( "platform_apps/web_view/speech_recognition_api", @@ -3867,7 +3761,7 @@ << message_; } -IN_PROC_BROWSER_TEST_P(WebViewSpeechAPITest, +IN_PROC_BROWSER_TEST_F(WebViewSpeechAPITest, SpeechRecognitionAPI_NoPermission) { ASSERT_TRUE( RunExtensionTest("platform_apps/web_view/common", @@ -3877,7 +3771,7 @@ } // Tests overriding user agent. -IN_PROC_BROWSER_TEST_P(WebViewTest, UserAgent) { +IN_PROC_BROWSER_TEST_F(WebViewTest, UserAgent) { ASSERT_TRUE(RunExtensionTest( "platform_apps/web_view/common", {.custom_arg = "useragent", .launch_as_platform_app = true})) @@ -3897,41 +3791,41 @@ << message_; } -IN_PROC_BROWSER_TEST_P(WebViewTest, NoPermission) { +IN_PROC_BROWSER_TEST_F(WebViewTest, NoPermission) { ASSERT_TRUE(RunExtensionTest("platform_apps/web_view/nopermission", {.launch_as_platform_app = true})) << message_; } -IN_PROC_BROWSER_TEST_P(WebViewTest, Dialog_TestAlertDialog) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Dialog_TestAlertDialog) { TestHelper("testAlertDialog", "web_view/dialog", NO_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, TestConfirmDialog) { +IN_PROC_BROWSER_TEST_F(WebViewTest, TestConfirmDialog) { TestHelper("testConfirmDialog", "web_view/dialog", NO_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, Dialog_TestConfirmDialogCancel) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Dialog_TestConfirmDialogCancel) { TestHelper("testConfirmDialogCancel", "web_view/dialog", NO_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, Dialog_TestConfirmDialogDefaultCancel) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Dialog_TestConfirmDialogDefaultCancel) { TestHelper("testConfirmDialogDefaultCancel", "web_view/dialog", NO_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, Dialog_TestConfirmDialogDefaultGCCancel) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Dialog_TestConfirmDialogDefaultGCCancel) { TestHelper("testConfirmDialogDefaultGCCancel", "web_view/dialog", NO_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, Dialog_TestPromptDialog) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Dialog_TestPromptDialog) { TestHelper("testPromptDialog", "web_view/dialog", NO_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, NoContentSettingsAPI) { +IN_PROC_BROWSER_TEST_F(WebViewTest, NoContentSettingsAPI) { // Load the extension. const extensions::Extension* content_settings_extension = LoadExtension( @@ -3951,11 +3845,6 @@ } }; -INSTANTIATE_TEST_SUITE_P(WebViewTests, - WebViewCaptureTest, - testing::Bool(), - WebViewTest::DescribeParams); - // TODO(crbug.com/1087381): Flaky on mac // TODO(crbug.com/1052397): Revisit once build flag switch of lacros-chrome is // complete. @@ -3966,11 +3855,11 @@ #else #define MAYBE_Shim_TestZoomAPI Shim_TestZoomAPI #endif -IN_PROC_BROWSER_TEST_P(WebViewTest, MAYBE_Shim_TestZoomAPI) { +IN_PROC_BROWSER_TEST_F(WebViewTest, MAYBE_Shim_TestZoomAPI) { TestHelper("testZoomAPI", "web_view/shim", NO_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestFindAPI) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestFindAPI) { TestHelper("testFindAPI", "web_view/shim", NO_TEST_SERVER); } @@ -3980,48 +3869,45 @@ #else #define MAYBE_Shim_TestFindAPI_findupdate Shim_TestFindAPI_findupdate #endif -IN_PROC_BROWSER_TEST_P(WebViewTest, MAYBE_Shim_TestFindAPI_findupdate) { +IN_PROC_BROWSER_TEST_F(WebViewTest, MAYBE_Shim_TestFindAPI_findupdate) { TestHelper("testFindAPI_findupdate", "web_view/shim", NO_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_testFindInMultipleWebViews) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_testFindInMultipleWebViews) { TestHelper("testFindInMultipleWebViews", "web_view/shim", NO_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestLoadDataAPI) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestLoadDataAPI) { TestHelper("testLoadDataAPI", "web_view/shim", NEEDS_TEST_SERVER); - // Ensure that when site-isolated guests are enabled, the guest process is - // locked after the loadDataWithBaseURL navigation and is allowed to access - // resources belonging to the base URL's origin. - if (content::SiteIsolationPolicy::IsSiteIsolationForGuestsEnabled()) { - content::RenderFrameHost* guest_main_frame = - GetGuestViewManager()->WaitForSingleGuestRenderFrameHostCreated(); - ASSERT_TRUE(guest_main_frame); - EXPECT_TRUE( - guest_main_frame->GetSiteInstance()->RequiresDedicatedProcess()); - EXPECT_TRUE( - guest_main_frame->GetProcess()->IsProcessLockedToSiteForTesting()); + // Ensure that the guest process is locked after the loadDataWithBaseURL + // navigation and is allowed to access resources belonging to the base URL's + // origin. + content::RenderFrameHost* guest_main_frame = + GetGuestViewManager()->WaitForSingleGuestRenderFrameHostCreated(); + ASSERT_TRUE(guest_main_frame); + EXPECT_TRUE(guest_main_frame->GetSiteInstance()->RequiresDedicatedProcess()); + EXPECT_TRUE( + guest_main_frame->GetProcess()->IsProcessLockedToSiteForTesting()); - auto* security_policy = content::ChildProcessSecurityPolicy::GetInstance(); - url::Origin base_origin = url::Origin::Create(GURL("http://localhost")); - EXPECT_TRUE(security_policy->CanAccessDataForOrigin( - guest_main_frame->GetProcess()->GetID(), base_origin)); + auto* security_policy = content::ChildProcessSecurityPolicy::GetInstance(); + url::Origin base_origin = url::Origin::Create(GURL("http://localhost")); + EXPECT_TRUE(security_policy->CanAccessDataForOrigin( + guest_main_frame->GetProcess()->GetID(), base_origin)); - // Ensure the process doesn't have access to some other origin. This - // verifies that site isolation is enforced. - url::Origin another_origin = url::Origin::Create(GURL("http://foo.com")); - EXPECT_FALSE(security_policy->CanAccessDataForOrigin( - guest_main_frame->GetProcess()->GetID(), another_origin)); - } + // Ensure the process doesn't have access to some other origin. This + // verifies that site isolation is enforced. + url::Origin another_origin = url::Origin::Create(GURL("http://foo.com")); + EXPECT_FALSE(security_policy->CanAccessDataForOrigin( + guest_main_frame->GetProcess()->GetID(), another_origin)); } -IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestLoadDataAPIAccessibleResources) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestLoadDataAPIAccessibleResources) { TestHelper("testLoadDataAPIAccessibleResources", "web_view/shim", NEEDS_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, LoadDataAPINotRelativeToAnotherExtension) { +IN_PROC_BROWSER_TEST_F(WebViewTest, LoadDataAPINotRelativeToAnotherExtension) { ASSERT_TRUE(StartEmbeddedTestServer()); const extensions::Extension* other_extension = LoadExtension(test_data_dir_.AppendASCII("simple_with_file")); @@ -4048,24 +3934,24 @@ } // This test verifies that the resize and contentResize events work correctly. -IN_PROC_BROWSER_TEST_P(WebViewSizeTest, Shim_TestResizeEvents) { +IN_PROC_BROWSER_TEST_F(WebViewSizeTest, Shim_TestResizeEvents) { TestHelper("testResizeEvents", "web_view/shim", NO_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestPerOriginZoomMode) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestPerOriginZoomMode) { TestHelper("testPerOriginZoomMode", "web_view/shim", NO_TEST_SERVER); } // TODO(crbug.com/935665): Test has flaky failures on all platforms. -IN_PROC_BROWSER_TEST_P(WebViewTest, DISABLED_Shim_TestPerViewZoomMode) { +IN_PROC_BROWSER_TEST_F(WebViewTest, DISABLED_Shim_TestPerViewZoomMode) { TestHelper("testPerViewZoomMode", "web_view/shim", NO_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestDisabledZoomMode) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestDisabledZoomMode) { TestHelper("testDisabledZoomMode", "web_view/shim", NO_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestZoomBeforeNavigation) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestZoomBeforeNavigation) { TestHelper("testZoomBeforeNavigation", "web_view/shim", NO_TEST_SERVER); } @@ -4163,13 +4049,8 @@ net::EmbeddedTestServer https_server_{net::EmbeddedTestServer::TYPE_HTTPS}; }; -INSTANTIATE_TEST_SUITE_P(WebViewTests, - WebViewCertificateSelectorTest, - testing::Bool(), - WebViewTest::DescribeParams); - // Ensure a guest triggering a client certificate dialog does not crash. -IN_PROC_BROWSER_TEST_P(WebViewCertificateSelectorTest, +IN_PROC_BROWSER_TEST_F(WebViewCertificateSelectorTest, CertificateSelectorForGuest) { LoadAppWithGuest("web_view/simple"); content::RenderFrameHost* guest_rfh = GetGuestRenderFrameHost(); @@ -4194,7 +4075,7 @@ // happened multiple times for various dialogs and signin flows (see // https://crbug.com/1076696 and https://crbug.com/1306988 ), so let's test that // if we are in this situation, we at least don't crash. -IN_PROC_BROWSER_TEST_P(WebViewCertificateSelectorTest, +IN_PROC_BROWSER_TEST_F(WebViewCertificateSelectorTest, CertificateSelectorForGuestMisconfigured) { LoadAppWithGuest("web_view/simple"); content::WebContents* guest = GetGuestWebContents(); @@ -4222,34 +4103,24 @@ } // Test fixture to run the test on multiple channels. -class WebViewChannelTest : public WebViewTestBase, - public testing::WithParamInterface< - testing::tuple<bool, version_info::Channel>> { +class WebViewChannelTest + : public WebViewTest, + public testing::WithParamInterface<version_info::Channel> { public: - WebViewChannelTest() : channel_(GetChannelParam()) { - scoped_feature_list_.InitWithFeatureState( - features::kSiteIsolationForGuests, - /*enabled=*/testing::get<0>(GetParam())); - } + WebViewChannelTest() : channel_(GetChannelParam()) {} - version_info::Channel GetChannelParam() { - return testing::get<1>(GetParam()); - } + version_info::Channel GetChannelParam() { return GetParam(); } + WebViewChannelTest(const WebViewChannelTest&) = delete; WebViewChannelTest& operator=(const WebViewChannelTest&) = delete; static std::string DescribeParams( const testing::TestParamInfo<ParamType>& info) { - auto [is_site_isolation_enabled, channel] = info.param; - return base::StringPrintf( - "SiteIsolationForGuests%s_%s", - is_site_isolation_enabled ? "Enabled" : "Disabled", - channel == version_info::Channel::STABLE ? "StableChannel" - : "NonStableChannel"); + return info.param == version_info::Channel::STABLE ? "StableChannel" + : "NonStableChannel"; } private: - base::test::ScopedFeatureList scoped_feature_list_; extensions::ScopedCurrentChannel channel_; }; @@ -4351,35 +4222,33 @@ registry->rules_cache_delegate_for_testing()->type()); } -INSTANTIATE_TEST_SUITE_P( - WebViewTests, - WebViewChannelTest, - testing::Combine(testing::Bool(), - testing::Values(version_info::Channel::UNKNOWN, - version_info::Channel::STABLE)), - WebViewChannelTest::DescribeParams); +INSTANTIATE_TEST_SUITE_P(WebViewTests, + WebViewChannelTest, + testing::Values(version_info::Channel::UNKNOWN, + version_info::Channel::STABLE), + WebViewChannelTest::DescribeParams); // This test verifies that webview.contentWindow works inside an iframe. -IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestWebViewInsideFrame) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestWebViewInsideFrame) { LoadAppWithGuest("web_view/inside_iframe"); } // <webview> screenshot capture fails with ubercomp. // See http://crbug.com/327035. -IN_PROC_BROWSER_TEST_P(WebViewCaptureTest, DISABLED_Shim_ScreenshotCapture) { +IN_PROC_BROWSER_TEST_F(WebViewCaptureTest, DISABLED_Shim_ScreenshotCapture) { TestHelper("testScreenshotCapture", "web_view/shim", NO_TEST_SERVER); } // Test is disabled because it times out often. // http://crbug.com/403325 -IN_PROC_BROWSER_TEST_P(WebViewTest, DISABLED_WebViewInBackgroundPage) { +IN_PROC_BROWSER_TEST_F(WebViewTest, DISABLED_WebViewInBackgroundPage) { ASSERT_TRUE(StartEmbeddedTestServer()); ASSERT_TRUE(RunExtensionTest("platform_apps/web_view/background")) << message_; } // This test verifies that the allowtransparency attribute properly propagates. -IN_PROC_BROWSER_TEST_P(WebViewTest, AllowTransparencyAndAllowScalingPropagate) { +IN_PROC_BROWSER_TEST_F(WebViewTest, AllowTransparencyAndAllowScalingPropagate) { LoadAppWithGuest("web_view/simple"); ASSERT_TRUE(GetGuestView()); @@ -4389,7 +4258,7 @@ ASSERT_TRUE(guest->allow_scaling()); } -IN_PROC_BROWSER_TEST_P(WebViewTest, BasicPostMessage) { +IN_PROC_BROWSER_TEST_F(WebViewTest, BasicPostMessage) { ASSERT_TRUE(StartEmbeddedTestServer()); // For serving guest pages. ASSERT_TRUE(RunExtensionTest("platform_apps/web_view/post_message/basic", {.launch_as_platform_app = true})) @@ -4397,12 +4266,12 @@ } // Tests that webviews do get garbage collected. -IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestGarbageCollect) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestGarbageCollect) { TestHelper("testGarbageCollect", "web_view/shim", NO_TEST_SERVER); GetGuestViewManager()->WaitForSingleViewGarbageCollected(); } -IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestCloseNewWindowCleanup) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestCloseNewWindowCleanup) { TestHelper("testCloseNewWindowCleanup", "web_view/shim", NEEDS_TEST_SERVER); auto* gvm = GetGuestViewManager(); gvm->WaitForLastGuestDeleted(); @@ -4411,18 +4280,14 @@ // Ensure that focusing a WebView while it is already focused does not blur the // guest content. -IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestFocusWhileFocused) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestFocusWhileFocused) { TestHelper("testFocusWhileFocused", "web_view/shim", NO_TEST_SERVER); } #if BUILDFLAG(ENABLE_PDF) using WebViewPdfTest = WebViewTest; -INSTANTIATE_TEST_SUITE_P(WebViewTests, - WebViewPdfTest, - testing::Bool(), - WebViewTest::DescribeParams); -IN_PROC_BROWSER_TEST_P(WebViewPdfTest, NestedGuestContainerBounds) { +IN_PROC_BROWSER_TEST_F(WebViewPdfTest, NestedGuestContainerBounds) { TestHelper("testPDFInWebview", "web_view/shim", NO_TEST_SERVER); std::vector<content::RenderFrameHost*> guest_rfh_list; @@ -4446,7 +4311,7 @@ // Test that context menu Back/Forward items in a MimeHandlerViewGuest affect // the embedder WebContents. See crbug.com/587355. -IN_PROC_BROWSER_TEST_P(WebViewPdfTest, ContextMenuNavigationInMimeHandlerView) { +IN_PROC_BROWSER_TEST_F(WebViewPdfTest, ContextMenuNavigationInMimeHandlerView) { TestHelper("testNavigateToPDFInWebview", "web_view/shim", NO_TEST_SERVER); GetGuestViewManager()->WaitForNumGuestsCreated(2u); @@ -4484,18 +4349,18 @@ EXPECT_EQ(GURL(url::kAboutBlankURL), web_view_rfh2->GetLastCommittedURL()); } -IN_PROC_BROWSER_TEST_P(WebViewPdfTest, Shim_TestDialogInPdf) { +IN_PROC_BROWSER_TEST_F(WebViewPdfTest, Shim_TestDialogInPdf) { TestHelper("testDialogInPdf", "web_view/shim", NO_TEST_SERVER); } #endif // BUILDFLAG(ENABLE_PDF) -IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestMailtoLink) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestMailtoLink) { TestHelper("testMailtoLink", "web_view/shim", NEEDS_TEST_SERVER); } // Tests that a renderer navigation from an unattached guest that results in a // server redirect works properly. -IN_PROC_BROWSER_TEST_P(WebViewTest, +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestRendererNavigationRedirectWhileUnattached) { TestHelper("testRendererNavigationRedirectWhileUnattached", "web_view/shim", NEEDS_TEST_SERVER); @@ -4505,25 +4370,25 @@ // See https://crbug.com/652077. // Also tests that the embedder can't navigate to a blob URL created by a // WebView. See https://crbug.com/1106890. -IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestBlobURL) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestBlobURL) { TestHelper("testBlobURL", "web_view/shim", NEEDS_TEST_SERVER); } // Tests that no error page is shown when WebRequest blocks a navigation. -IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestWebRequestBlockedNavigation) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestWebRequestBlockedNavigation) { TestHelper("testWebRequestBlockedNavigation", "web_view/shim", NEEDS_TEST_SERVER); } // Tests that a WebView accessible resource can actually be loaded from a // webpage in a WebView. -IN_PROC_BROWSER_TEST_P(WebViewTest, LoadWebviewAccessibleResource) { +IN_PROC_BROWSER_TEST_F(WebViewTest, LoadWebviewAccessibleResource) { TestHelper("testLoadWebviewAccessibleResource", "web_view/load_webview_accessible_resource", NEEDS_TEST_SERVER); } // Tests that a WebView can be navigated to a WebView accessible resource. -IN_PROC_BROWSER_TEST_P(WebViewTest, NavigateGuestToWebviewAccessibleResource) { +IN_PROC_BROWSER_TEST_F(WebViewTest, NavigateGuestToWebviewAccessibleResource) { TestHelper("testNavigateGuestToWebviewAccessibleResource", "web_view/load_webview_accessible_resource", NO_TEST_SERVER); @@ -4551,7 +4416,7 @@ // Tests that a WebView can reload a WebView accessible resource. See // https://crbug.com/691941. -IN_PROC_BROWSER_TEST_P(WebViewTest, ReloadWebviewAccessibleResource) { +IN_PROC_BROWSER_TEST_F(WebViewTest, ReloadWebviewAccessibleResource) { TestHelper("testReloadWebviewAccessibleResource", "web_view/load_webview_accessible_resource", NEEDS_TEST_SERVER); @@ -4570,7 +4435,7 @@ // Tests that a WebView can navigate an iframe to a blob URL that it creates // while its main frame is at a WebView accessible resource. -IN_PROC_BROWSER_TEST_P(WebViewTest, BlobInWebviewAccessibleResource) { +IN_PROC_BROWSER_TEST_F(WebViewTest, BlobInWebviewAccessibleResource) { TestHelper("testBlobInWebviewAccessibleResource", "web_view/load_webview_accessible_resource", NEEDS_TEST_SERVER); @@ -4599,7 +4464,7 @@ // Tests that a WebView cannot load a webview-inaccessible resource. See // https://crbug.com/640072. -IN_PROC_BROWSER_TEST_P(WebViewTest, LoadWebviewInaccessibleResource) { +IN_PROC_BROWSER_TEST_F(WebViewTest, LoadWebviewInaccessibleResource) { TestHelper("testLoadWebviewInaccessibleResource", "web_view/load_webview_accessible_resource", NEEDS_TEST_SERVER); @@ -4620,12 +4485,12 @@ // Ensure that only app resources accessible to the webview can be loaded in a // webview even if the webview commits an app frame. -IN_PROC_BROWSER_TEST_P(WebViewTest, +IN_PROC_BROWSER_TEST_F(WebViewTest, LoadAccessibleSubresourceInAppWebviewFrame) { TestHelper("testLoadAccessibleSubresourceInAppWebviewFrame", "web_view/load_webview_accessible_resource", NEEDS_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewTest, +IN_PROC_BROWSER_TEST_F(WebViewTest, InaccessibleResourceDoesNotLoadInAppWebviewFrame) { TestHelper("testInaccessibleResourceDoesNotLoadInAppWebviewFrame", "web_view/load_webview_accessible_resource", NEEDS_TEST_SERVER); @@ -4633,7 +4498,7 @@ // Makes sure that a webview will display correctly after reloading it after a // crash. -IN_PROC_BROWSER_TEST_P(WebViewTest, ReloadAfterCrash) { +IN_PROC_BROWSER_TEST_F(WebViewTest, ReloadAfterCrash) { // Load guest and wait for it to appear. LoadAppWithGuest("web_view/simple"); @@ -4683,17 +4548,12 @@ } }; -INSTANTIATE_TEST_SUITE_P(WebViewTests, - WebViewTestNoDomAutomationController, - testing::Bool(), - WebViewTest::DescribeParams); - // Tests that a webview inside an iframe can load and that it is destroyed when // the iframe is detached. // We need to disable DomAutomationController because it forces the creation of // a script context. We want to test that we handle the case where there is no // script context for the iframe. See crbug.com/788914 -IN_PROC_BROWSER_TEST_P(WebViewTestNoDomAutomationController, +IN_PROC_BROWSER_TEST_F(WebViewTestNoDomAutomationController, LoadWebviewInsideIframe) { TestHelper("testLoadWebviewInsideIframe", "web_view/load_webview_inside_iframe", NEEDS_TEST_SERVER); @@ -4708,7 +4568,7 @@ GetGuestViewManager()->WaitForLastGuestDeleted(); } -IN_PROC_BROWSER_TEST_P(WebViewAccessibilityTest, LoadWebViewAccessibility) { +IN_PROC_BROWSER_TEST_F(WebViewAccessibilityTest, LoadWebViewAccessibility) { content::BrowserAccessibilityState::GetInstance()->EnableAccessibility(); LoadAppWithGuest("web_view/focus_accessibility"); content::WebContents* web_contents = GetFirstAppWindowWebContents(); @@ -4716,7 +4576,7 @@ "Guest button"); } -IN_PROC_BROWSER_TEST_P(WebViewAccessibilityTest, FocusAccessibility) { +IN_PROC_BROWSER_TEST_F(WebViewAccessibilityTest, FocusAccessibility) { content::BrowserAccessibilityState::GetInstance()->EnableAccessibility(); LoadAppWithGuest("web_view/focus_accessibility"); content::WebContents* web_contents = GetFirstAppWindowWebContents(); @@ -4750,7 +4610,7 @@ // BrowserAccessibilityManager would not be updated due to how we were updating // the AXTreeData. // The test was disabled. See crbug.com/1141313. -IN_PROC_BROWSER_TEST_P(WebViewAccessibilityTest, +IN_PROC_BROWSER_TEST_F(WebViewAccessibilityTest, DISABLED_FocusAccessibilityNestedFrame) { content::BrowserAccessibilityState::GetInstance()->EnableAccessibility(); LoadAppWithGuest("web_view/focus_accessibility"); @@ -4833,7 +4693,7 @@ size_t count_; }; -IN_PROC_BROWSER_TEST_P(WebViewAccessibilityTest, DISABLED_TouchAccessibility) { +IN_PROC_BROWSER_TEST_F(WebViewAccessibilityTest, DISABLED_TouchAccessibility) { content::BrowserAccessibilityState::GetInstance()->EnableAccessibility(); LoadAppWithGuest("web_view/touch_accessibility"); content::WebContents* web_contents = GetFirstAppWindowWebContents(); @@ -4866,29 +4726,17 @@ EXPECT_EQ(0U, main_event_watcher.count()); } -class WebViewGuestScrollTest - : public WebViewTestBase, - public testing::WithParamInterface<testing::tuple<bool, bool>> { +class WebViewGuestScrollTest : public WebViewTest, + public testing::WithParamInterface<bool> { public: - WebViewGuestScrollTest() { - scoped_feature_list_.InitWithFeatureState( - features::kSiteIsolationForGuests, - /*enabled=*/testing::get<0>(GetParam())); - } - - bool GetScrollParam() { return testing::get<1>(GetParam()); } + bool GetScrollParam() { return GetParam(); } static std::string DescribeParams( const testing::TestParamInfo<ParamType>& info) { - auto [is_site_isolation_enabled, is_scroll_disabled] = info.param; - return base::StringPrintf( - "SiteIsolationForGuests%s_Scroll%s", - is_site_isolation_enabled ? "Enabled" : "Disabled", - is_scroll_disabled ? "Disabled" : "Enabled"); + bool is_scroll_disabled = info.param; + return base::StringPrintf("Scroll%s", + is_scroll_disabled ? "Disabled" : "Enabled"); } - - private: - base::test::ScopedFeatureList scoped_feature_list_; }; class WebViewGuestScrollTouchTest : public WebViewGuestScrollTest { @@ -4908,7 +4756,7 @@ // different ack results in between these two cases. INSTANTIATE_TEST_SUITE_P(WebViewScrollBubbling, WebViewGuestScrollTest, - testing::Combine(testing::Bool(), testing::Bool()), + testing::Bool(), WebViewGuestScrollTest::DescribeParams); IN_PROC_BROWSER_TEST_P(WebViewGuestScrollTest, TestGuestWheelScrollsBubble) { @@ -5073,7 +4921,7 @@ INSTANTIATE_TEST_SUITE_P(WebViewScrollBubbling, WebViewGuestScrollTouchTest, - testing::Combine(testing::Bool(), testing::Bool()), + testing::Bool(), WebViewGuestScrollTouchTest::DescribeParams); IN_PROC_BROWSER_TEST_P(WebViewGuestScrollTouchTest, @@ -5182,14 +5030,9 @@ } }; -INSTANTIATE_TEST_SUITE_P(WebViewTests, - ChromeSignInWebViewTest, - testing::Bool(), - WebViewTest::DescribeParams); - #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) // This verifies the fix for http://crbug.com/667708. -IN_PROC_BROWSER_TEST_P(ChromeSignInWebViewTest, +IN_PROC_BROWSER_TEST_F(ChromeSignInWebViewTest, ClosingChromeSignInShouldNotCrash) { GURL signin_url{"chrome://chrome-signin/?reason=5"}; @@ -5216,7 +5059,7 @@ #else #define MAYBE_NoFindInPageForUnattachedGuest NoFindInPageForUnattachedGuest #endif -IN_PROC_BROWSER_TEST_P(ChromeSignInWebViewTest, +IN_PROC_BROWSER_TEST_F(ChromeSignInWebViewTest, MAYBE_NoFindInPageForUnattachedGuest) { GURL signin_url{"chrome://chrome-signin/?reason=5"}; ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), signin_url)); @@ -5289,16 +5132,11 @@ } }; -INSTANTIATE_TEST_SUITE_P(WebViewTests, - IsolatedOriginWebViewTest, - testing::Bool(), - WebViewTest::DescribeParams); - // Test isolated origins inside a WebView, and make sure that loading an // isolated origin in a regular tab's subframe doesn't reuse a WebView process // that had loaded it previously, which would result in renderer kills. See // https://crbug.com/751916 and https://crbug.com/751920. -IN_PROC_BROWSER_TEST_P(IsolatedOriginWebViewTest, IsolatedOriginInWebview) { +IN_PROC_BROWSER_TEST_F(IsolatedOriginWebViewTest, IsolatedOriginInWebview) { LoadAppWithGuest("web_view/simple"); guest_view::GuestViewBase* guest = GetGuestView(); @@ -5330,22 +5168,14 @@ EXPECT_TRUE(NavigateToURLFromRenderer( ChildFrameAt(guest->GetGuestMainFrame(), 0), isolated_url)); - // If site isolation for <webview> is not used, the subframe will stay in the - // guest process and SiteInstance. Otherwise, it will be in its own + // Since <webview> supports site isolation, the subframe will be in its own // SiteInstance and process. content::RenderFrameHost* webview_subframe = ChildFrameAt(guest->GetGuestMainFrame(), 0); - if (content::SiteIsolationPolicy::IsSiteIsolationForGuestsEnabled()) { - EXPECT_NE(webview_subframe->GetProcess(), - guest->GetGuestMainFrame()->GetProcess()); - EXPECT_NE(webview_subframe->GetSiteInstance(), - guest->GetGuestMainFrame()->GetSiteInstance()); - } else { - EXPECT_EQ(webview_subframe->GetProcess(), - guest->GetGuestMainFrame()->GetProcess()); - EXPECT_EQ(webview_subframe->GetSiteInstance(), - guest->GetGuestMainFrame()->GetSiteInstance()); - } + EXPECT_NE(webview_subframe->GetProcess(), + guest->GetGuestMainFrame()->GetProcess()); + EXPECT_NE(webview_subframe->GetSiteInstance(), + guest->GetGuestMainFrame()->GetSiteInstance()); // Load a page with subframe in a regular tab. ASSERT_TRUE(AddTabAtIndex(0, foo_url, ui::PAGE_TRANSITION_TYPED)); @@ -5380,7 +5210,7 @@ // regular tab's subframe. The isolated origin's subframe in the <webview> // subframe should not reuse the regular tab's subframe process. See // https://crbug.com/751916 and https://crbug.com/751920. -IN_PROC_BROWSER_TEST_P(IsolatedOriginWebViewTest, +IN_PROC_BROWSER_TEST_F(IsolatedOriginWebViewTest, LoadIsolatedOriginInWebviewAfterLoadingInRegularTab) { LoadAppWithGuest("web_view/simple"); guest_view::GuestViewBase* guest = GetGuestView(); @@ -5410,22 +5240,14 @@ EXPECT_TRUE(NavigateToURLFromRenderer( ChildFrameAt(guest->GetGuestMainFrame(), 0), isolated_url)); - // If site isolation for <webview> is not used, the subframe will stay in the - // guest process and SiteInstance. Otherwise, it will be in its own + // Since <webview> supports site isolation, the subframe will be in its own // SiteInstance and process. content::RenderFrameHost* webview_subframe = ChildFrameAt(guest->GetGuestMainFrame(), 0); - if (content::SiteIsolationPolicy::IsSiteIsolationForGuestsEnabled()) { - EXPECT_NE(webview_subframe->GetProcess(), - guest->GetGuestMainFrame()->GetProcess()); - EXPECT_NE(webview_subframe->GetSiteInstance(), - guest->GetGuestMainFrame()->GetSiteInstance()); - } else { - EXPECT_EQ(webview_subframe->GetProcess(), - guest->GetGuestMainFrame()->GetProcess()); - EXPECT_EQ(webview_subframe->GetSiteInstance(), - guest->GetGuestMainFrame()->GetSiteInstance()); - } + EXPECT_NE(webview_subframe->GetProcess(), + guest->GetGuestMainFrame()->GetProcess()); + EXPECT_NE(webview_subframe->GetSiteInstance(), + guest->GetGuestMainFrame()->GetSiteInstance()); // The isolated origin subframe in <webview> shouldn't share the process with // the isolated origin subframe in the regular tab. @@ -5451,7 +5273,7 @@ // Sends an auto-resize message to the RenderWidgetHost and ensures that the // auto-resize transaction is handled and produces a single response message // from guest to embedder. -IN_PROC_BROWSER_TEST_P(WebViewTest, AutoResizeMessages) { +IN_PROC_BROWSER_TEST_F(WebViewTest, AutoResizeMessages) { LoadAppWithGuest("web_view/simple"); // Helper function as this test requires inspecting a number of content:: @@ -5461,7 +5283,7 @@ } // Test that a guest sees the synthetic wheel events of a touchpad pinch. -IN_PROC_BROWSER_TEST_P(WebViewTest, TouchpadPinchSyntheticWheelEvents) { +IN_PROC_BROWSER_TEST_F(WebViewTest, TouchpadPinchSyntheticWheelEvents) { ASSERT_TRUE(StartEmbeddedTestServer()); LoadAppWithGuest("web_view/touchpad_pinch"); @@ -5490,7 +5312,7 @@ // Tests that we can open and close a devtools window that inspects a contents // containing a guest view without crashing. -IN_PROC_BROWSER_TEST_P(WebViewTest, OpenAndCloseDevTools) { +IN_PROC_BROWSER_TEST_F(WebViewTest, OpenAndCloseDevTools) { LoadAppWithGuest("web_view/simple"); content::WebContents* embedder = GetEmbedderWebContents(); DevToolsWindow* devtools = DevToolsWindowTesting::OpenDevToolsWindowSync( @@ -5501,7 +5323,7 @@ // Tests that random extensions cannot inject content scripts into a platform // app's own webview, but the owner platform app can. Regression test for // crbug.com/1205675. -IN_PROC_BROWSER_TEST_P(WebViewTest, NoExtensionScriptsInjectedInWebview) { +IN_PROC_BROWSER_TEST_F(WebViewTest, NoExtensionScriptsInjectedInWebview) { ASSERT_TRUE(StartEmbeddedTestServer()); // For serving guest pages. // Load an extension which injects a content script at document_end. The @@ -5557,10 +5379,6 @@ private: base::test::ScopedFeatureList features_; }; -INSTANTIATE_TEST_SUITE_P(WebViewTests, - LocalNetworkAccessWebViewTest, - testing::Bool(), - WebViewTest::DescribeParams); // Verify that Local Network Access has the correct understanding of guests. // The chrome-guest:// scheme should only ever be used as a Site URL (and only @@ -5571,7 +5389,7 @@ // Note: This test is put in this file for convenience of reusing the entire // app testing infrastructure. Other similar tests that do not require that // infrastructure live in LocalNetworkAccessBrowserTest.* -IN_PROC_BROWSER_TEST_P(LocalNetworkAccessWebViewTest, +IN_PROC_BROWSER_TEST_F(LocalNetworkAccessWebViewTest, SpecialSchemeChromeGuest) { LoadAppWithGuest("web_view/simple"); content::RenderFrameHost* guest_frame_host = GetGuestRenderFrameHost(); @@ -5602,7 +5420,7 @@ // Verify that navigating a <webview> subframe to a disallowed extension // resource (where the extension ID doesn't match the <webview> owner) doesn't // result in a renderer kill. See https://crbug.com/1204094. -IN_PROC_BROWSER_TEST_P(WebViewTest, LoadDisallowedExtensionURLInSubframe) { +IN_PROC_BROWSER_TEST_F(WebViewTest, LoadDisallowedExtensionURLInSubframe) { ASSERT_TRUE(StartEmbeddedTestServer()); base::RunLoop run_loop; identifiability_metrics_test_helper_.PrepareForTest(&run_loop); @@ -5659,7 +5477,7 @@ entry->metrics.begin()->second); } -IN_PROC_BROWSER_TEST_P(WebViewTest, InsertIntoDetachedIframe) { +IN_PROC_BROWSER_TEST_F(WebViewTest, InsertIntoDetachedIframe) { TestHelper("testInsertIntoDetachedIframe", "web_view/shim", NEEDS_TEST_SERVER); // Round-trip to ensure the embedder did not crash. @@ -5675,16 +5493,11 @@ } }; -INSTANTIATE_TEST_SUITE_P(WebViewTests, - WebViewPPAPITest, - testing::Bool(), - WebViewTest::DescribeParams); - -IN_PROC_BROWSER_TEST_P(WebViewPPAPITest, Shim_TestPlugin) { +IN_PROC_BROWSER_TEST_F(WebViewPPAPITest, Shim_TestPlugin) { TestHelper("testPlugin", "web_view/shim", NO_TEST_SERVER); } -IN_PROC_BROWSER_TEST_P(WebViewPPAPITest, Shim_TestPluginLoadPermission) { +IN_PROC_BROWSER_TEST_F(WebViewPPAPITest, Shim_TestPluginLoadPermission) { TestHelper("testPluginLoadPermission", "web_view/shim", NO_TEST_SERVER); } #endif // BUILDFLAG(ENABLE_PPAPI) @@ -5697,17 +5510,13 @@ constexpr char kWebstoreURLOverride[] = "https://webstore.override.test.com/"; // Helper class for setting up and testing webview behavior with the Chrome -// Webstore. The tuple param contains the Webstore URL under test and a boolen -// that is passed to the base class to set if site isolation is enabled for -// guests. -class WebstoreWebViewTest - : public WebViewGuestSiteIsolationTest, - public testing::WithParamInterface<std::tuple<GURL, bool>> { +// Webstore. The test param contains the Webstore URL to test. +class WebstoreWebViewTest : public WebViewTest, + public testing::WithParamInterface<GURL> { public: WebstoreWebViewTest() - : WebViewGuestSiteIsolationTest(::testing::get<1>(GetParam())), - https_server_(net::EmbeddedTestServer::TYPE_HTTPS), - webstore_url_(std::move(::testing::get<0>(GetParam()))) {} + : https_server_(net::EmbeddedTestServer::TYPE_HTTPS), + webstore_url_(GetParam()) {} WebstoreWebViewTest(const WebstoreWebViewTest&) = delete; WebstoreWebViewTest& operator=(const WebstoreWebViewTest&) = delete; @@ -5730,22 +5539,22 @@ kWebstoreURLOverride); } mock_cert_verifier_.SetUpCommandLine(command_line); - WebViewGuestSiteIsolationTest::SetUpCommandLine(command_line); + WebViewTest::SetUpCommandLine(command_line); } void SetUpOnMainThread() override { https_server_.StartAcceptingConnections(); mock_cert_verifier_.mock_cert_verifier()->set_default_result(net::OK); - WebViewGuestSiteIsolationTest::SetUpOnMainThread(); + WebViewTest::SetUpOnMainThread(); } void SetUpInProcessBrowserTestFixture() override { - WebViewGuestSiteIsolationTest::SetUpInProcessBrowserTestFixture(); + WebViewTest::SetUpInProcessBrowserTestFixture(); mock_cert_verifier_.SetUpInProcessBrowserTestFixture(); } void TearDownInProcessBrowserTestFixture() override { - WebViewGuestSiteIsolationTest::TearDownInProcessBrowserTestFixture(); + WebViewTest::TearDownInProcessBrowserTestFixture(); mock_cert_verifier_.TearDownInProcessBrowserTestFixture(); } @@ -5753,17 +5562,13 @@ GURL webstore_url() { return webstore_url_; } // Provides meaningful param names. - static std::string DescribeParams( - const testing::TestParamInfo<std::tuple<GURL, bool>>& info) { - std::string guest_isolation = ::testing::get<1>(info.param) - ? "SiteIsolationForGuestsEnabled" - : "SiteIsolationForGuestsDisabled"; - GURL webstore_url(std::move(::testing::get<0>(info.param))); + static std::string DescribeParams(const testing::TestParamInfo<GURL>& info) { + GURL webstore_url(info.param); if (webstore_url.spec() == kWebstoreURL) - return "OldWebstore_" + guest_isolation; + return "OldWebstore"; if (webstore_url.spec() == kWebstoreURLOverride) - return "WebstoreOverride_" + guest_isolation; - return "NewWebstore_" + guest_isolation; + return "WebstoreOverride"; + return "NewWebstore"; } private: @@ -5772,14 +5577,12 @@ GURL webstore_url_; }; -INSTANTIATE_TEST_SUITE_P( - WebViewTests, - WebstoreWebViewTest, - testing::Combine(testing::Values(GURL(kWebstoreURL), - GURL(kWebstoreURLOverride), - GURL(kNewWebstoreURL)), - testing::Bool()), - WebstoreWebViewTest::DescribeParams); +INSTANTIATE_TEST_SUITE_P(WebViewTests, + WebstoreWebViewTest, + testing::Values(GURL(kWebstoreURL), + GURL(kWebstoreURLOverride), + GURL(kNewWebstoreURL)), + WebstoreWebViewTest::DescribeParams); // Ensure that an attempt to load Chrome Web Store in a <webview> is blocked // and does not result in a renderer kill. See https://crbug.com/1197674. @@ -5817,21 +5620,9 @@ EXPECT_EQ(false, content::EvalJs(guest, "!!chrome.dashboardPrivate")); } -// This is a base class for tests that enable site isolation in <webview> -// guests. -class SitePerProcessWebViewTest : public WebViewTestBase { - public: - SitePerProcessWebViewTest() { - feature_list_.InitAndEnableFeature(features::kSiteIsolationForGuests); - } - ~SitePerProcessWebViewTest() override = default; - SitePerProcessWebViewTest(const SitePerProcessWebViewTest&) = delete; - SitePerProcessWebViewTest& operator=(const SitePerProcessWebViewTest&) = - delete; - - private: - base::test::ScopedFeatureList feature_list_; -}; +// This is a group of tests that check site isolation properties in <webview> +// guests. Note that site isolation in <webview> is always enabled. +using SitePerProcessWebViewTest = WebViewTest; // Checks basic site isolation properties when a <webview> main frame and // subframe navigate cross-site. @@ -6622,30 +6413,12 @@ EXPECT_EQ(fenced_frame->GetProcess(), guest_rfh->GetProcess()); } -class WebViewFencedFrameTest - : public WebViewTestBase, - public testing::WithParamInterface<std::tuple<bool, bool>> { +class WebViewFencedFrameTest : public WebViewTest, + public testing::WithParamInterface<bool> { public: WebViewFencedFrameTest() { - bool should_enable_site_isolation_for_guests = std::get<0>(GetParam()); - bool should_enable_process_isolation_for_fenced_frames = - std::get<1>(GetParam()); - std::vector<base::test::FeatureRef> enabled_features, disabled_features; - - if (should_enable_site_isolation_for_guests) { - enabled_features.push_back(features::kSiteIsolationForGuests); - } else { - disabled_features.push_back(features::kSiteIsolationForGuests); - } - - if (should_enable_process_isolation_for_fenced_frames) { - enabled_features.push_back(features::kIsolateFencedFrames); - } else { - disabled_features.push_back(features::kIsolateFencedFrames); - } - - scoped_feature_list_.InitWithFeatures(std::move(enabled_features), - std::move(disabled_features)); + scoped_feature_list_.InitWithFeatureState(features::kIsolateFencedFrames, + /*enabled=*/GetParam()); } ~WebViewFencedFrameTest() override = default; @@ -6655,12 +6428,8 @@ static std::string DescribeParams( const testing::TestParamInfo<ParamType>& info) { - return base::StringPrintf( - "%s_%s", - std::get<0>(info.param) ? "SiteIsolationForGuestsEnabled" - : "SiteIsolationForGuestsDisabled", - std::get<1>(info.param) ? "IsolateFencedFramesEnabled" - : "IsolateFencedFramesDisabled"); + return info.param ? "IsolateFencedFramesEnabled" + : "IsolateFencedFramesDisabled"; } private: @@ -6670,7 +6439,7 @@ INSTANTIATE_TEST_SUITE_P(WebViewTests, WebViewFencedFrameTest, - testing::Combine(testing::Bool(), testing::Bool()), + testing::Bool(), WebViewFencedFrameTest::DescribeParams); IN_PROC_BROWSER_TEST_P(WebViewFencedFrameTest, @@ -6692,10 +6461,8 @@ guest_rfh->GetSiteInstance()->GetStoragePartitionConfig()); // The fenced frame will be in a different process from the embedding guest - // only if both Site Isolation for Guests and Process Isolation for Fenced - // Frames are enabled. - if (content::SiteIsolationPolicy::IsSiteIsolationForGuestsEnabled() && - content::SiteIsolationPolicy:: + // only if Process Isolation for Fenced Frames is enabled. + if (content::SiteIsolationPolicy:: IsProcessIsolationForFencedFramesEnabled()) { EXPECT_NE(ff_rfh->GetProcess(), guest_rfh->GetProcess()); } else { @@ -6723,12 +6490,7 @@ base::test::ScopedFeatureList scoped_feature_list_; }; -INSTANTIATE_TEST_SUITE_P(WebViewTests, - WebViewPortalTest, - testing::Bool(), - WebViewTest::DescribeParams); - // Creates and activates a <portal> element inside a <webview>. -IN_PROC_BROWSER_TEST_P(WebViewPortalTest, PortalActivationInGuest) { +IN_PROC_BROWSER_TEST_F(WebViewPortalTest, PortalActivationInGuest) { TestHelper("testActivatePortal", "web_view/shim", NEEDS_TEST_SERVER); }
diff --git a/chrome/browser/apps/guest_view/web_view_interactive_browsertest.cc b/chrome/browser/apps/guest_view/web_view_interactive_browsertest.cc index 185ffa6..a0cd986 100644 --- a/chrome/browser/apps/guest_view/web_view_interactive_browsertest.cc +++ b/chrome/browser/apps/guest_view/web_view_interactive_browsertest.cc
@@ -153,6 +153,11 @@ command_line->AppendSwitch(blink::switches::kAllowPreCommitInput); } + void SetUpOnMainThread() override { + host_resolver()->AddRule("*", "127.0.0.1"); + extensions::PlatformAppBrowserTest::SetUpOnMainThread(); + } + TestGuestViewManager* GetGuestViewManager() { TestGuestViewManager* manager = static_cast<TestGuestViewManager*>( TestGuestViewManager::FromBrowserContext(browser()->profile())); @@ -1658,34 +1663,9 @@ } #endif -// Base class for interactive tests that enable site isolation in <webview> -// guests. -class SitePerProcessWebViewInteractiveTest : public WebViewInteractiveTest { - public: - SitePerProcessWebViewInteractiveTest() = default; - ~SitePerProcessWebViewInteractiveTest() override = default; - SitePerProcessWebViewInteractiveTest( - const SitePerProcessWebViewInteractiveTest&) = delete; - SitePerProcessWebViewInteractiveTest& operator=( - const SitePerProcessWebViewInteractiveTest&) = delete; - - void SetUp() override { - feature_list_.InitAndEnableFeature(features::kSiteIsolationForGuests); - WebViewInteractiveTest::SetUp(); - } - - void SetUpOnMainThread() override { - host_resolver()->AddRule("*", "127.0.0.1"); - WebViewInteractiveTest::SetUpOnMainThread(); - } - - private: - base::test::ScopedFeatureList feature_list_; -}; - // Check that when a focused <webview> navigates cross-process, the focus // is preserved in the new page. See https://crbug.com/1358210. -IN_PROC_BROWSER_TEST_F(SitePerProcessWebViewInteractiveTest, +IN_PROC_BROWSER_TEST_F(WebViewInteractiveTest, FocusPreservedAfterCrossProcessNavigation) { // Load and show a platform app with a <webview> on a data: URL. ASSERT_TRUE(StartEmbeddedTestServer());
diff --git a/chrome/browser/ash/BUILD.gn b/chrome/browser/ash/BUILD.gn index aa1ae38..a268f58 100644 --- a/chrome/browser/ash/BUILD.gn +++ b/chrome/browser/ash/BUILD.gn
@@ -983,6 +983,8 @@ "dbus/vm/vm_permission_service_provider.h", "dbus/vm/vm_sk_forwarding_service_provider.cc", "dbus/vm/vm_sk_forwarding_service_provider.h", + "dbus/vm/vm_wl_service_provider.cc", + "dbus/vm/vm_wl_service_provider.h", "device_name/device_name_applier.h", "device_name/device_name_applier_impl.cc", "device_name/device_name_applier_impl.h", @@ -2495,6 +2497,8 @@ "policy/reporting/metrics_reporting/apps/app_platform_metrics_retriever.h", "policy/reporting/metrics_reporting/apps/app_usage_collector.cc", "policy/reporting/metrics_reporting/apps/app_usage_collector.h", + "policy/reporting/metrics_reporting/apps/app_usage_telemetry_sampler.cc", + "policy/reporting/metrics_reporting/apps/app_usage_telemetry_sampler.h", "policy/reporting/metrics_reporting/audio/audio_events_observer.cc", "policy/reporting/metrics_reporting/audio/audio_events_observer.h", "policy/reporting/metrics_reporting/cros_healthd_metric_sampler.cc", @@ -3419,6 +3423,7 @@ "//chromeos/ash/components/dbus:metrics_event_proto", "//chromeos/ash/components/dbus:vm_applications_apps_proto", "//chromeos/ash/components/dbus:vm_launch_proto", + "//chromeos/ash/components/dbus:vm_wl_proto", "//chromeos/ash/components/dbus/anomaly_detector", "//chromeos/ash/components/dbus/anomaly_detector:proto", "//chromeos/ash/components/dbus/attestation", @@ -4167,6 +4172,7 @@ "dbus/vm/org.chromium.VmLaunchService.conf", "dbus/vm/org.chromium.VmPermissionService.conf", "dbus/vm/org.chromium.VmSKForwardingService.conf", + "dbus/vm/org.chromium.VmWlService.conf", ] output_conf_file = "$root_out_dir/dbus/chrome_dbus_services.conf" outputs = [ output_conf_file ] @@ -5471,6 +5477,7 @@ "policy/reporting/metrics_reporting/apps/app_events_observer_unittest.cc", "policy/reporting/metrics_reporting/apps/app_platform_metrics_retriever_unittest.cc", "policy/reporting/metrics_reporting/apps/app_usage_collector_unittest.cc", + "policy/reporting/metrics_reporting/apps/app_usage_telemetry_sampler_unittest.cc", "policy/reporting/metrics_reporting/audio/audio_events_observer_unittest.cc", "policy/reporting/metrics_reporting/cros_healthd_metric_sampler_unittest.cc", "policy/reporting/metrics_reporting/cros_reporting_settings_unittest.cc", @@ -5656,6 +5663,8 @@ "throttle_service_unittest.cc", "tpm_firmware_update_unittest.cc", "usb/cros_usb_detector_unittest.cc", + "video_conference/video_conference_manager_ash_unittest.cc", + "wallpaper_handlers/wallpaper_handlers_unittest.cc", "web_applications/face_ml/chrome_face_ml_user_provider_unittest.cc", "web_applications/help_app/help_app_discover_tab_notification_unittest.cc", "web_applications/help_app/help_app_notification_controller_unittest.cc",
diff --git a/chrome/browser/ash/app_list/search/system_info/system_info_card_provider.cc b/chrome/browser/ash/app_list/search/system_info/system_info_card_provider.cc index 50378f7..8cca2543 100644 --- a/chrome/browser/ash/app_list/search/system_info/system_info_card_provider.cc +++ b/chrome/browser/ash/app_list/search/system_info/system_info_card_provider.cc
@@ -321,7 +321,10 @@ const absl::optional<power_manager::PowerSupplyProperties>& proto = chromeos::PowerManagerClient::Get()->GetLastStatus(); - DCHECK(proto); + if (!proto) { + EmitBatteryDataError(BatteryDataError::kNoData); + return; + } PopulatePowerStatus(proto.value(), *new_battery_health.get());
diff --git a/chrome/browser/ash/app_list/search/system_info/system_info_card_provider_unittest.cc b/chrome/browser/ash/app_list/search/system_info/system_info_card_provider_unittest.cc index bd6117f6..9cc67b7 100644 --- a/chrome/browser/ash/app_list/search/system_info/system_info_card_provider_unittest.cc +++ b/chrome/browser/ash/app_list/search/system_info/system_info_card_provider_unittest.cc
@@ -16,7 +16,9 @@ #include "base/path_service.h" #include "base/strings/strcat.h" #include "base/system/sys_info.h" +#include "base/test/metrics/histogram_tester.h" #include "base/test/scoped_running_on_chromeos.h" +#include "chrome/browser/ash/app_list/search/system_info/system_info_util.h" #include "chrome/browser/ash/app_list/search/test/test_search_controller.h" #include "chrome/browser/ash/file_manager/fake_disk_mount_manager.h" #include "chrome/browser/ash/file_manager/path_util.h" @@ -44,6 +46,16 @@ namespace healthd_mojom = ash::cros_healthd::mojom; +constexpr char kBatteryDataError[] = + "Apps.AppList.SystemInfoProvider.Error.Battery"; + +constexpr char kProbeErrorBatteryInfo[] = + "Apps.AppList.SystemInfoProvider.CrosHealthdProbeError.BatteryInfo"; +constexpr char kProbeErrorCpuInfo[] = + "Apps.AppList.SystemInfoProvider.CrosHealthdProbeError.CpuInfo"; +constexpr char kProbeErrorMemoryInfo[] = + "Apps.AppList.SystemInfoProvider.CrosHealthdProbeError.MemoryInfo"; + void SetProbeTelemetryInfoResponse(healthd_mojom::BatteryInfoPtr battery_info, healthd_mojom::CpuInfoPtr cpu_info, healthd_mojom::MemoryInfoPtr memory_info) { @@ -252,6 +264,50 @@ ASSERT_EQ(expected_size, stat.st_size); } +healthd_mojom::ProbeErrorPtr CreateProbeError( + healthd_mojom::ErrorType error_type) { + auto probe_error = healthd_mojom::ProbeError::New(); + probe_error->type = error_type; + probe_error->msg = "probe error"; + return probe_error; +} + +void VerifyProbeErrorBucketCounts(const base::HistogramTester& tester, + const std::string& metric_name, + size_t expected_unknown_error, + size_t expected_parse_error, + size_t expected_service_unavailable, + size_t expected_system_utility_error, + size_t expected_file_read_error) { + tester.ExpectBucketCount(metric_name, healthd_mojom::ErrorType::kUnknown, + expected_unknown_error); + tester.ExpectBucketCount(metric_name, healthd_mojom::ErrorType::kParseError, + expected_parse_error); + tester.ExpectBucketCount(metric_name, + healthd_mojom::ErrorType::kServiceUnavailable, + expected_service_unavailable); + tester.ExpectBucketCount(metric_name, + healthd_mojom::ErrorType::kSystemUtilityError, + expected_system_utility_error); + tester.ExpectBucketCount(metric_name, + healthd_mojom::ErrorType::kFileReadError, + expected_file_read_error); +} + +void VerifyBatteryDataErrorBucketCounts( + const base::HistogramTester& tester, + size_t expected_no_data_error, + size_t expected_not_a_number_error, + size_t expected_expectation_not_met_error) { + tester.ExpectBucketCount(kBatteryDataError, BatteryDataError::kNoData, + expected_no_data_error); + tester.ExpectBucketCount(kBatteryDataError, BatteryDataError::kNotANumber, + expected_not_a_number_error); + tester.ExpectBucketCount(kBatteryDataError, + BatteryDataError::kExpectationNotMet, + expected_expectation_not_met_error); +} + } // namespace class SystemInfoCardProviderTest : public testing::Test { @@ -323,7 +379,7 @@ std::unique_ptr<SystemInfoCardProvider> provider_; }; -TEST_F(SystemInfoCardProviderTest, version) { +TEST_F(SystemInfoCardProviderTest, Version) { StartSearch(u"version"); Wait(); std::u16string official = @@ -361,7 +417,7 @@ EXPECT_TRUE(details.GetTextTags().empty()); } -TEST_F(SystemInfoCardProviderTest, cpu) { +TEST_F(SystemInfoCardProviderTest, Cpu) { int temp_1 = 40; int temp_2 = 50; int temp_3 = 15; @@ -400,7 +456,29 @@ EXPECT_TRUE(details.GetTextTags().empty()); } -TEST_F(SystemInfoCardProviderTest, memory) { +TEST_F(SystemInfoCardProviderTest, CpuProbeError) { + auto info = healthd_mojom::TelemetryInfo::New(); + base::HistogramTester histogram_tester; + + auto cpu_result = healthd_mojom::CpuResult::NewError( + CreateProbeError(healthd_mojom::ErrorType::kFileReadError)); + info->cpu_result = std::move(cpu_result); + ash::cros_healthd::FakeCrosHealthd::Get() + ->SetProbeTelemetryInfoResponseForTesting(info); + + StartSearch(u"cpu"); + Wait(); + + EXPECT_TRUE(results().empty()); + VerifyProbeErrorBucketCounts(histogram_tester, kProbeErrorCpuInfo, + /*expected_unknown_error=*/0, + /*expected_parse_error=*/0, + /*expected_service_unavailable=*/0, + /*expected_system_utility_error=*/0, + /*expected_file_read_error=*/1); +} + +TEST_F(SystemInfoCardProviderTest, Memory) { const uint32_t total_memory_kib = 8000000; const uint32_t free_memory_kib = 2000000; const uint32_t available_memory_kib = 4000000; @@ -425,18 +503,40 @@ ASSERT_EQ(results()[0]->title_text_vector().size(), 1u); const auto& title = results()[0]->title_text_vector()[0]; - ASSERT_EQ(title.GetType(), ash::SearchResultTextItemType::kString); + EXPECT_EQ(title.GetType(), ash::SearchResultTextItemType::kString); EXPECT_EQ(title.GetText(), u""); EXPECT_TRUE(title.GetTextTags().empty()); ASSERT_EQ(results()[0]->details_text_vector().size(), 1u); const auto& details = results()[0]->details_text_vector()[0]; - ASSERT_EQ(details.GetType(), ash::SearchResultTextItemType::kString); + EXPECT_EQ(details.GetType(), ash::SearchResultTextItemType::kString); EXPECT_EQ(details.GetText(), u"3.8 GB of 7.6 GB available"); EXPECT_TRUE(details.GetTextTags().empty()); } -TEST_F(SystemInfoCardProviderTest, battery) { +TEST_F(SystemInfoCardProviderTest, MemoryProbeError) { + auto info = healthd_mojom::TelemetryInfo::New(); + base::HistogramTester histogram_tester; + + auto memory_result = healthd_mojom::MemoryResult::NewError( + CreateProbeError(healthd_mojom::ErrorType::kSystemUtilityError)); + info->memory_result = std::move(memory_result); + ash::cros_healthd::FakeCrosHealthd::Get() + ->SetProbeTelemetryInfoResponseForTesting(info); + + StartSearch(u"memory"); + Wait(); + + EXPECT_TRUE(results().empty()); + VerifyProbeErrorBucketCounts(histogram_tester, kProbeErrorMemoryInfo, + /*expected_unknown_error=*/0, + /*expected_parse_error=*/0, + /*expected_service_unavailable=*/0, + /*expected_system_utility_error=*/1, + /*expected_file_read_error=*/0); +} + +TEST_F(SystemInfoCardProviderTest, Battery) { const double charge_full_now = 20; const double charge_full_design = 26; const int32_t cycle_count = 500; @@ -503,7 +603,95 @@ EXPECT_TRUE(updated_title.GetTextTags().empty()); } -TEST_F(SystemInfoCardProviderTest, storage) { +TEST_F(SystemInfoCardProviderTest, BatteryProbeError) { + auto info = healthd_mojom::TelemetryInfo::New(); + base::HistogramTester histogram_tester; + + auto battery_result = healthd_mojom::BatteryResult::NewError( + CreateProbeError(healthd_mojom::ErrorType::kParseError)); + info->battery_result = std::move(battery_result); + ash::cros_healthd::FakeCrosHealthd::Get() + ->SetProbeTelemetryInfoResponseForTesting(info); + + const auto power_source = + power_manager::PowerSupplyProperties_ExternalPower_AC; + const auto battery_state = + power_manager::PowerSupplyProperties_BatteryState_CHARGING; + const bool is_calculating_battery_time = false; + const int64_t time_to_full_secs = 1000; + const int64_t time_to_empty_secs = 0; + const double battery_percent = 94.0; + + SetPowerManagerProperties(power_source, battery_state, + is_calculating_battery_time, time_to_full_secs, + time_to_empty_secs, battery_percent); + + StartSearch(u"battery"); + Wait(); + + VerifyProbeErrorBucketCounts(histogram_tester, kProbeErrorBatteryInfo, + /*expected_unknown_error=*/0, + /*expected_parse_error=*/1, + /*expected_service_unavailable=*/0, + /*expected_system_utility_error=*/0, + /*expected_file_read_error=*/0); + EXPECT_TRUE(results().empty()); +} + +TEST_F(SystemInfoCardProviderTest, BatteryProbeDataError) { + auto info = healthd_mojom::TelemetryInfo::New(); + base::HistogramTester histogram_tester; + + SetCrosHealthdBatteryHealthResponse(0, 0, 0); + + const auto power_source = + power_manager::PowerSupplyProperties_ExternalPower_AC; + const auto battery_state = + power_manager::PowerSupplyProperties_BatteryState_CHARGING; + const bool is_calculating_battery_time = false; + const int64_t time_to_full_secs = 1000; + const int64_t time_to_empty_secs = 0; + const double battery_percent = 94.0; + + SetPowerManagerProperties(power_source, battery_state, + is_calculating_battery_time, time_to_full_secs, + time_to_empty_secs, battery_percent); + + StartSearch(u"battery"); + Wait(); + + VerifyBatteryDataErrorBucketCounts(histogram_tester, + /*expected_no_data_error=*/0, + /*expected_not_a_number_error=*/0, + /*expected_expectation_not_met_error=*/1); + EXPECT_TRUE(results().empty()); +} + +TEST_F(SystemInfoCardProviderTest, BatteryPowerManagerError) { + auto info = healthd_mojom::TelemetryInfo::New(); + base::HistogramTester histogram_tester; + + const double charge_full_now = 20; + const double charge_full_design = 26; + const int32_t cycle_count = 500; + + SetCrosHealthdBatteryHealthResponse(charge_full_now, charge_full_design, + cycle_count); + + absl::nullopt_t props = absl::nullopt; + chromeos::FakePowerManagerClient::Get()->UpdatePowerProperties(props); + + StartSearch(u"battery"); + Wait(); + + VerifyBatteryDataErrorBucketCounts(histogram_tester, + /*expected_no_data_error=*/1, + /*expected_not_a_number_error=*/0, + /*expected_expectation_not_met_error=*/0); + EXPECT_TRUE(results().empty()); +} + +TEST_F(SystemInfoCardProviderTest, Storage) { base::ScopedAllowBlockingForTesting allow_blocking; // Get local filesystem storage statistics.
diff --git a/chrome/browser/ash/app_list/search/system_info/system_info_util.cc b/chrome/browser/ash/app_list/search/system_info/system_info_util.cc index adb67c45..809a480 100644 --- a/chrome/browser/ash/app_list/search/system_info/system_info_util.cc +++ b/chrome/browser/ash/app_list/search/system_info/system_info_util.cc
@@ -25,20 +25,6 @@ constexpr int kMilliampsInAnAmp = 1000; -// The enums below are used in histograms, do not remove/renumber entries. If -// you're adding to any of these enums, update the corresponding enum listing in -// tools/metrics/histograms/enums.xml: CrosDiagnosticsDataError. -enum class DataError { - // Null or nullptr value. - kNoData = 0, - // For numeric values that are NaN. - kNotANumber = 1, - // Expectation about data not met. Ex. routing prefix is between zero and - // thirty-two. - kExpectationNotMet = 2, - kMaxValue = kExpectationNotMet, -}; - const std::string GetMetricNameForSourceType( const base::StringPiece source_type) { if (source_type == "cpu info") { @@ -70,11 +56,6 @@ base::UmaHistogramEnumeration(metric_name, error_type); } -void EmitBatteryDataError(DataError error) { - base::UmaHistogramEnumeration("Apps.AppList.SystemInfoProvider.Error.Battery", - error); -} - template <typename TResult, typename TTag> bool CheckResponse(const TResult& result, @@ -100,6 +81,11 @@ } // namespace +void EmitBatteryDataError(BatteryDataError error) { + base::UmaHistogramEnumeration("Apps.AppList.SystemInfoProvider.Error.Battery", + error); +} + healthd::MemoryInfo* GetMemoryInfo(const healthd::TelemetryInfo& info) { const healthd::MemoryResultPtr& memory_result = info.memory_result; if (!CheckResponse(memory_result, healthd::MemoryResult::Tag::kMemoryInfo, @@ -117,7 +103,25 @@ return nullptr; } - return battery_result->get_battery_info().get(); + const healthd::BatteryInfo* battery_info = + battery_result->get_battery_info().get(); + if (battery_info->charge_full == 0) { + LOG(ERROR) << "charge_full from battery_info should not be zero."; + EmitBatteryDataError(BatteryDataError::kExpectationNotMet); + return nullptr; + } + + // Handle values in battery_info which could cause a SIGFPE. See b/227485637. + if (isnan(battery_info->charge_full) || + isnan(battery_info->charge_full_design) || + battery_info->charge_full_design == 0) { + LOG(ERROR) << "battery_info values could cause SIGFPE crash: { " + << "charge_full_design: " << battery_info->charge_full_design + << ", charge_full: " << battery_info->charge_full << " }"; + return nullptr; + } + + return battery_info; } healthd::CpuInfo* GetCpuInfo(const healthd::TelemetryInfo& info) { @@ -195,27 +199,10 @@ total_scaled_ghz / cpu_info.physical_cpus[0]->logical_cpus.size()); } -void PopulateBatteryHealth( - const ash::cros_healthd::mojom::BatteryInfo& battery_info, - BatteryHealth& battery_health) { +void PopulateBatteryHealth(const healthd::BatteryInfo& battery_info, + BatteryHealth& battery_health) { battery_health.SetCycleCount(battery_info.cycle_count); - if (battery_info.charge_full == 0) { - LOG(ERROR) << "charge_full from battery_info should not be zero."; - EmitBatteryDataError(DataError::kExpectationNotMet); - } - - // Handle values in battery_info which could cause a SIGFPE. See b/227485637. - if (isnan(battery_info.charge_full) || - isnan(battery_info.charge_full_design) || - battery_info.charge_full_design == 0) { - LOG(ERROR) << "battery_info values could cause SIGFPE crash: { " - << "charge_full_design: " << battery_info.charge_full_design - << ", charge_full: " << battery_info.charge_full << " }"; - battery_health.SetBatteryWearPercentage(0); - return; - } - double charge_full_now_milliamp_hours = battery_info.charge_full * kMilliampsInAnAmp; double charge_full_design_milliamp_hours =
diff --git a/chrome/browser/ash/app_list/search/system_info/system_info_util.h b/chrome/browser/ash/app_list/search/system_info/system_info_util.h index dfab86f..eba57e5 100644 --- a/chrome/browser/ash/app_list/search/system_info/system_info_util.h +++ b/chrome/browser/ash/app_list/search/system_info/system_info_util.h
@@ -17,6 +17,22 @@ namespace app_list { +// The enums below are used in histograms, do not remove/renumber entries. If +// you're adding to any of these enums, update the corresponding enum listing in +// tools/metrics/histograms/enums.xml: CrosDiagnosticsDataError. +enum class BatteryDataError { + // Null or nullptr value. + kNoData = 0, + // For numeric values that are NaN. + kNotANumber = 1, + // Expectation about data not met. Ex. routing prefix is between zero and + // thirty-two. + kExpectationNotMet = 2, + kMaxValue = kExpectationNotMet, +}; + +void EmitBatteryDataError(BatteryDataError error); + // Extracts MemoryInfo from `info`. Logs and returns a nullptr if MemoryInfo // in not present. ash::cros_healthd::mojom::MemoryInfo* GetMemoryInfo(
diff --git a/chrome/browser/ash/arc/accessibility/arc_accessibility_helper_bridge_browsertest.cc b/chrome/browser/ash/arc/accessibility/arc_accessibility_helper_bridge_browsertest.cc index de850c1..31c4518 100644 --- a/chrome/browser/ash/arc/accessibility/arc_accessibility_helper_bridge_browsertest.cc +++ b/chrome/browser/ash/arc/accessibility/arc_accessibility_helper_bridge_browsertest.cc
@@ -195,17 +195,24 @@ IN_PROC_BROWSER_TEST_F(ArcAccessibilityHelperBridgeBrowserTest, RequestTreeSyncOnWindowIdChange) { auto shell_surface1 = MakeTestArcWindow("org.chromium.arc.1"); + aura::Window* window1 = shell_surface1->GetWidget()->GetNativeWindow(); + aura::Window child_window1 = aura::Window(nullptr); + child_window1.Init(ui::LAYER_NOT_DRAWN); + window1->AddChild(&child_window1); + auto shell_surface2 = MakeTestArcWindow("org.chromium.arc.2"); + aura::Window* window2 = shell_surface2->GetWidget()->GetNativeWindow(); + aura::Window child_window2 = aura::Window(nullptr); + child_window2.Init(ui::LAYER_NOT_DRAWN); + window2->AddChild(&child_window2); wm::ActivationClient* activation_client = ash::Shell::Get()->activation_client(); - activation_client->ActivateWindow( - shell_surface1->GetWidget()->GetNativeWindow()); + activation_client->ActivateWindow(window1); AccessibilityManager::Get()->EnableSpokenFeedback(true); - exo::SetShellClientAccessibilityId( - shell_surface1->GetWidget()->GetNativeWindow(), 10); + exo::SetShellClientAccessibilityId(&child_window1, 10); EXPECT_TRUE( fake_accessibility_helper_instance_->last_requested_tree_window_key() @@ -214,15 +221,13 @@ 10U, fake_accessibility_helper_instance_->last_requested_tree_window_key() ->get_window_id()); - exo::SetShellClientAccessibilityId( - shell_surface2->GetWidget()->GetNativeWindow(), 20); + exo::SetShellClientAccessibilityId(&child_window2, 20); EXPECT_EQ( 20U, fake_accessibility_helper_instance_->last_requested_tree_window_key() ->get_window_id()); - exo::SetShellClientAccessibilityId( - shell_surface2->GetWidget()->GetNativeWindow(), 21); + exo::SetShellClientAccessibilityId(&child_window2, 21); EXPECT_EQ( 21U, fake_accessibility_helper_instance_->last_requested_tree_window_key()
diff --git a/chrome/browser/ash/arc/accessibility/arc_accessibility_tree_tracker.cc b/chrome/browser/ash/arc/accessibility/arc_accessibility_tree_tracker.cc index e0172ec8..91c1fa5 100644 --- a/chrome/browser/ash/arc/accessibility/arc_accessibility_tree_tracker.cc +++ b/chrome/browser/ash/arc/accessibility/arc_accessibility_tree_tracker.cc
@@ -185,10 +185,7 @@ void OnWindowPropertyChanged(aura::Window* window, const void* key, intptr_t old) override { - // TODO(b/270904414): remove kClientAccessibilityIdKey once sending a11y id - // is fully migrated to per-window level. - if (key != exo::kApplicationIdKey && - key != ash::kClientAccessibilityIdKey) { + if (key != exo::kApplicationIdKey) { return; } owner_->UpdateTopWindowIds(window); @@ -840,24 +837,17 @@ if (!task_id.has_value()) return; - if (task_id_to_window_.count(task_id.value()) == 0) { - task_id_to_window_.emplace(task_id.value(), window); - - // Force re-evaluate children so that window_id and task_id are correctly - // mapped. - for (aura::Window* child : window->children()) { - TrackChildWindow(child); - } - } - - // TODO(b/270904414): remove a11y window id check on top window once sending - // a11y id is fully migrated to per-window level. - const auto window_id = exo::GetShellClientAccessibilityId(window); - if (!window_id.has_value()) { + if (task_id_to_window_.count(task_id.value()) > 0) { + // We already know this task id. return; } + task_id_to_window_.emplace(task_id.value(), window); - UpdateWindowIdAndTaskId(window_id.value(), task_id.value()); + // Force re-evaluate children so that window_id and task_id are correctly + // mapped. + for (aura::Window* child : window->children()) { + TrackChildWindow(child); + } } void ArcAccessibilityTreeTracker::UpdateChildWindowIds(aura::Window* window) { @@ -865,6 +855,10 @@ if (!window_id.has_value()) { return; } + if (window_id_to_task_id_.find(*window_id) != window_id_to_task_id_.end()) { + // We already know this window ID. + return; + } aura::Window* parent = FindArcWindow(window); auto task_id = GetWindowTaskId(parent); @@ -872,21 +866,11 @@ return; } - UpdateWindowIdAndTaskId(window_id.value(), task_id.value()); -} - -void ArcAccessibilityTreeTracker::UpdateWindowIdAndTaskId(int32_t window_id, - int32_t task_id) { - if (window_id_to_task_id_.find(window_id) != window_id_to_task_id_.end()) { - // We already know this window ID. - return; - } - - window_id_to_task_id_[window_id] = task_id; + window_id_to_task_id_[*window_id] = *task_id; // The window ID is new to us. Request the entire tree. arc::mojom::AccessibilityWindowKeyPtr window_key = - arc::mojom::AccessibilityWindowKey::NewWindowId(window_id); + arc::mojom::AccessibilityWindowKey::NewWindowId(*window_id); accessibility_helper_instance_.RequestSendAccessibilityTree( std::move(window_key)); }
diff --git a/chrome/browser/ash/arc/accessibility/arc_accessibility_tree_tracker.h b/chrome/browser/ash/arc/accessibility/arc_accessibility_tree_tracker.h index 48408d1..8d9b23c 100644 --- a/chrome/browser/ash/arc/accessibility/arc_accessibility_tree_tracker.h +++ b/chrome/browser/ash/arc/accessibility/arc_accessibility_tree_tracker.h
@@ -147,9 +147,6 @@ // window change. void UpdateChildWindowIds(aura::Window* window); - // Should be notified for mapping from |window_id| to |task_id|. - void UpdateWindowIdAndTaskId(int32_t window_id, int32_t task_id); - // Updates properties set to the given aura::Window. void UpdateWindowProperties(aura::Window* window);
diff --git a/chrome/browser/ash/arc/accessibility/arc_accessibility_tree_tracker_unittest.cc b/chrome/browser/ash/arc/accessibility/arc_accessibility_tree_tracker_unittest.cc index 265c4ab6..267299b 100644 --- a/chrome/browser/ash/arc/accessibility/arc_accessibility_tree_tracker_unittest.cc +++ b/chrome/browser/ash/arc/accessibility/arc_accessibility_tree_tracker_unittest.cc
@@ -243,10 +243,14 @@ // A ghost window is replaced with an actual ARC window. exo::SetShellApplicationId(test_window.get(), "org.chromium.arc.1"); - exo::SetShellClientAccessibilityId(test_window.get(), 10); test_window->SetProperty(aura::client::kAppType, static_cast<int>(ash::AppType::ARC_APP)); + std::unique_ptr<aura::Window> child_window = + CreateWindow(ash::AppType::NON_APP); + exo::SetShellClientAccessibilityId(child_window.get(), 10); + test_window->AddChild(child_window.get()); + tree_tracker.OnAccessibilityEvent(event.Clone().get()); ASSERT_EQ(1U, key_to_tree.size()); }
diff --git a/chrome/browser/ash/chrome_browser_main_parts_ash.cc b/chrome/browser/ash/chrome_browser_main_parts_ash.cc index e001074..1d025a5 100644 --- a/chrome/browser/ash/chrome_browser_main_parts_ash.cc +++ b/chrome/browser/ash/chrome_browser_main_parts_ash.cc
@@ -89,6 +89,7 @@ #include "chrome/browser/ash/dbus/vm/vm_launch_service_provider.h" #include "chrome/browser/ash/dbus/vm/vm_permission_service_provider.h" #include "chrome/browser/ash/dbus/vm/vm_sk_forwarding_service_provider.h" +#include "chrome/browser/ash/dbus/vm/vm_wl_service_provider.h" #include "chrome/browser/ash/device_name/device_name_store.h" #include "chrome/browser/ash/diagnostics/diagnostics_browser_delegate_impl.h" #include "chrome/browser/ash/display/quirks_manager_delegate_impl.h" @@ -266,6 +267,7 @@ #include "services/audio/public/cpp/sounds/sounds_manager.h" #include "third_party/cros_system_api/dbus/service_constants.h" #include "third_party/cros_system_api/dbus/vm_launch/dbus-constants.h" +#include "third_party/cros_system_api/dbus/vm_wl/dbus-constants.h" #include "ui/base/emoji/emoji_panel_helper.h" #include "ui/base/ime/ash/ime_keyboard.h" #include "ui/base/ime/ash/input_method_manager.h" @@ -455,6 +457,12 @@ CrosDBusService::CreateServiceProviderList( std::make_unique<VmPermissionServiceProvider>())); + vm_wl_service_ = CrosDBusService::Create( + system_bus, vm_tools::wl::kVmWlServiceName, + dbus::ObjectPath(vm_tools::wl::kVmWlServicePath), + CrosDBusService::CreateServiceProviderList( + std::make_unique<VmWlServiceProvider>())); + drive_file_stream_service_ = CrosDBusService::Create( system_bus, drivefs::kDriveFileStreamServiceName, dbus::ObjectPath(drivefs::kDriveFileStreamServicePath), @@ -572,6 +580,7 @@ vm_launch_service_.reset(); vm_sk_forwarding_service_.reset(); vm_permission_service_.reset(); + vm_wl_service_.reset(); drive_file_stream_service_.reset(); cryptohome_key_delegate_service_.reset(); encrypted_reporting_service_.reset(); @@ -605,6 +614,7 @@ std::unique_ptr<CrosDBusService> vm_launch_service_; std::unique_ptr<CrosDBusService> vm_sk_forwarding_service_; std::unique_ptr<CrosDBusService> vm_permission_service_; + std::unique_ptr<CrosDBusService> vm_wl_service_; std::unique_ptr<CrosDBusService> drive_file_stream_service_; std::unique_ptr<CrosDBusService> cryptohome_key_delegate_service_; std::unique_ptr<CrosDBusService> encrypted_reporting_service_;
diff --git a/chrome/browser/ash/crosapi/browser_data_migrator.cc b/chrome/browser/ash/crosapi/browser_data_migrator.cc index 6b5afc7..5e1e1b3 100644 --- a/chrome/browser/ash/crosapi/browser_data_migrator.cc +++ b/chrome/browser/ash/crosapi/browser_data_migrator.cc
@@ -218,20 +218,26 @@ // Check if user exists i.e. not a guest session. if (!user) return false; - // Check if lacros is enabled. If not immediately return. - if (!crosapi::browser_util::IsLacrosEnabledForMigration(user, - policy_init_state)) { - if (base::CommandLine::ForCurrentProcess()->HasSwitch( + // Check if profile migration is enabled. If not immediately return. + if (!crosapi::browser_util:: + IsProfileMigrationEnabledWithUserAndPolicyInitState( + user, policy_init_state)) { + if (crosapi::browser_util::IsLacrosEnabledForMigration(user, + policy_init_state) || + base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kSafeMode)) { - // Skip clearing of flags if in safe mode to make sure - // that the migrator does not wipe Lacros user data dir due to unexpected - // Ash crashes. Specifically this avoids the following scenario: Ash - // experiences a crash loop due to some experimental flag -> experimental - // flags get dropped including ones to enable Lacros -> Lacros is - // disabled and migration completion flags gets cleared -> on next login - // migration is run and wipes existing user data. - LOG(WARNING) << "Lacros is disabled but safe mode is enabled so skipping " - "clearing of prefs."; + // Skip clearing prefs if Lacros is enabled or Lacros is disabled due to + // safe mode. Profile migration can be disabled even if Lacros is enabled + // by enabling LacrosProfileMigrationForceOff flag. There's another case + // where Lacros is disabled due to "safe mode" being enabled after Ash + // crashes. By not clearing prefs in safe mode, we avoid the following + // scenario: Ash experiences a crash loop due to some experimental flag -> + // experimental flags get dropped including ones to enable Lacros -> + // Lacros is disabled and migration completion flags gets cleared -> on + // next login migration is run and wipes existing user data. + LOG(WARNING) + << "Profile migration is disabled but either Lacros is enabled or " + "safe mode is enabled so skipping clearing prefs."; return false; } @@ -239,7 +245,7 @@ // this log message. LOG(WARNING) << "Lacros is disabled. Call ClearMigrationAttemptCountForUser() so " - "that the migration can be attempted again after once lacros is " + "that the migration can be attempted again once migration is " "enabled again."; // If lacros is not enabled other than reaching the maximum retry count of @@ -254,14 +260,6 @@ return false; } - if (base::FeatureList::IsEnabled( - ash::features::kLacrosProfileMigrationForceOff)) { - // TODO(crbug.com/1277848): Once `BrowserDataMigrator` stabilises, remove - // this log message. - LOG(WARNING) << "Profile migration is disabled by a flag."; - return false; - } - int attempts = GetMigrationAttemptCountForUser(local_state, user_id_hash); // TODO(crbug.com/1178702): Once BrowserDataMigrator stabilises, reduce the // log level to VLOG(1).
diff --git a/chrome/browser/ash/crosapi/browser_util.cc b/chrome/browser/ash/crosapi/browser_util.cc index 58cecc41f..40302cd 100644 --- a/chrome/browser/ash/crosapi/browser_util.cc +++ b/chrome/browser/ash/crosapi/browser_util.cc
@@ -427,11 +427,6 @@ } bool IsProfileMigrationEnabled() { - if (base::FeatureList::IsEnabled( - ash::features::kLacrosProfileMigrationForceOff)) { - return false; - } - const UserManager* user_manager = UserManager::Get(); if (!user_manager) { return false; @@ -442,7 +437,16 @@ return false; } - return IsLacrosEnabledForMigration(user, PolicyInitState::kAfterInit); + return IsProfileMigrationEnabledWithUserAndPolicyInitState( + user, PolicyInitState::kAfterInit); +} + +bool IsProfileMigrationEnabledWithUserAndPolicyInitState( + const user_manager::User* user, + PolicyInitState policy_init_state) { + return !base::FeatureList::IsEnabled( + ash::features::kLacrosProfileMigrationForceOff) && + IsLacrosEnabledForMigration(user, policy_init_state); } bool IsProfileMigrationAvailable() {
diff --git a/chrome/browser/ash/crosapi/browser_util.h b/chrome/browser/ash/crosapi/browser_util.h index 84fbb521..229c3a6 100644 --- a/chrome/browser/ash/crosapi/browser_util.h +++ b/chrome/browser/ash/crosapi/browser_util.h
@@ -404,6 +404,10 @@ // enabled, the completion of it is required to enable Lacros. bool IsProfileMigrationEnabled(); +bool IsProfileMigrationEnabledWithUserAndPolicyInitState( + const user_manager::User* user, + PolicyInitState policy_init_state); + // Returns true if the profile migration is enabled, but not yet completed. bool IsProfileMigrationAvailable();
diff --git a/chrome/browser/ash/dbus/vm/org.chromium.VmWlService.conf b/chrome/browser/ash/dbus/vm/org.chromium.VmWlService.conf new file mode 100644 index 0000000..0cee728 --- /dev/null +++ b/chrome/browser/ash/dbus/vm/org.chromium.VmWlService.conf
@@ -0,0 +1,22 @@ +<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN" + "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd"> +<!-- + 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. +--> + +<busconfig> + <policy user="chronos"> + <allow own="org.chromium.VmWlService"/> + </policy> + + <policy user="crosvm"> + <allow send_destination="org.chromium.VmWlService" + send_interface="org.chromium.VmWlService" + send_member="ListenOnSocket"/> + <allow send_destination="org.chromium.VmWlService" + send_interface="org.chromium.VmWlService" + send_member="CloseSocket"/> + </policy> +</busconfig>
diff --git a/chrome/browser/ash/dbus/vm/vm_wl_service_provider.cc b/chrome/browser/ash/dbus/vm/vm_wl_service_provider.cc new file mode 100644 index 0000000..fee7144b --- /dev/null +++ b/chrome/browser/ash/dbus/vm/vm_wl_service_provider.cc
@@ -0,0 +1,107 @@ +// 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/ash/dbus/vm/vm_wl_service_provider.h" + +#include "base/files/scoped_file.h" +#include "base/logging.h" +#include "chrome/browser/ash/guest_os/public/guest_os_wayland_server.h" +#include "chromeos/ash/components/dbus/vm_wl/wl.pb.h" +#include "dbus/message.h" +#include "third_party/cros_system_api/dbus/vm_wl/dbus-constants.h" + +namespace ash { + +namespace { + +void OnExported(const std::string& interface_name, + const std::string& method_name, + bool success) { + LOG_IF(ERROR, !success) << "Failed to export " << interface_name << "." + << method_name; +} + +void Respond(dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender, + absl::optional<std::string> maybe_error) { + if (maybe_error) { + std::move(response_sender) + .Run(dbus::ErrorResponse::FromMethodCall(method_call, DBUS_ERROR_FAILED, + maybe_error.value())); + return; + } + std::move(response_sender).Run(dbus::Response::FromMethodCall(method_call)); +} + +} // namespace + +VmWlServiceProvider::VmWlServiceProvider() = default; + +VmWlServiceProvider::~VmWlServiceProvider() = default; + +void VmWlServiceProvider::Start( + scoped_refptr<dbus::ExportedObject> exported_object) { + exported_object->ExportMethod( + vm_tools::wl::kVmWlServiceInterface, + vm_tools::wl::kVmWlServiveListenOnSocketMethod, + base::BindRepeating(&VmWlServiceProvider::ListenOnSocket, + weak_ptr_factory_.GetWeakPtr()), + base::BindOnce(&OnExported)); + + exported_object->ExportMethod( + vm_tools::wl::kVmWlServiceInterface, + vm_tools::wl::kVmWlServiceCloseSocketMethod, + base::BindRepeating(&VmWlServiceProvider::CloseSocket, + weak_ptr_factory_.GetWeakPtr()), + base::BindOnce(&OnExported)); +} + +void VmWlServiceProvider::ListenOnSocket( + dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender) { + dbus::MessageReader reader(method_call); + + vm_tools::wl::ListenOnSocketRequest request; + if (!reader.PopArrayOfBytesAsProto(&request)) { + std::move(response_sender) + .Run(dbus::ErrorResponse::FromMethodCall( + method_call, DBUS_ERROR_INVALID_ARGS, + "Unable to parse ListenOnSocketRequest from message")); + return; + } + + base::ScopedFD socket_fd; + if (!reader.PopFileDescriptor(&socket_fd)) { + std::move(response_sender) + .Run(dbus::ErrorResponse::FromMethodCall( + method_call, DBUS_ERROR_INVALID_ARGS, + "Unable to parse socket fd from message")); + return; + } + + guest_os::GuestOsWaylandServer::ListenOnSocket( + request, std::move(socket_fd), + base::BindOnce(&Respond, method_call, std::move(response_sender))); +} + +void VmWlServiceProvider::CloseSocket( + dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender) { + dbus::MessageReader reader(method_call); + + vm_tools::wl::CloseSocketRequest request; + if (!reader.PopArrayOfBytesAsProto(&request)) { + std::move(response_sender) + .Run(dbus::ErrorResponse::FromMethodCall( + method_call, DBUS_ERROR_INVALID_ARGS, + "Unable to parse CloseSocketRequest from message")); + return; + } + + guest_os::GuestOsWaylandServer::CloseSocket( + request, + base::BindOnce(&Respond, method_call, std::move(response_sender))); +} + +} // namespace ash
diff --git a/chrome/browser/ash/dbus/vm/vm_wl_service_provider.h b/chrome/browser/ash/dbus/vm/vm_wl_service_provider.h new file mode 100644 index 0000000..7c11a7f --- /dev/null +++ b/chrome/browser/ash/dbus/vm/vm_wl_service_provider.h
@@ -0,0 +1,38 @@ +// 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_ASH_DBUS_VM_VM_WL_SERVICE_PROVIDER_H_ +#define CHROME_BROWSER_ASH_DBUS_VM_VM_WL_SERVICE_PROVIDER_H_ + +#include "chromeos/ash/components/dbus/services/cros_dbus_service.h" + +#include "dbus/exported_object.h" + +namespace ash { + +class VmWlServiceProvider : public CrosDBusService::ServiceProviderInterface { + public: + VmWlServiceProvider(); + ~VmWlServiceProvider() override; + + // Delete copy constructor/assign. + VmWlServiceProvider(const VmWlServiceProvider&) = delete; + VmWlServiceProvider& operator=(const VmWlServiceProvider&) = delete; + + // CrosDBusService::ServiceProviderInterface overrides: + void Start(scoped_refptr<dbus::ExportedObject> exported_object) override; + + private: + void ListenOnSocket(dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender); + + void CloseSocket(dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender); + + base::WeakPtrFactory<VmWlServiceProvider> weak_ptr_factory_{this}; +}; + +} // namespace ash + +#endif // CHROME_BROWSER_ASH_DBUS_VM_VM_WL_SERVICE_PROVIDER_H_
diff --git a/chrome/browser/ash/extensions/file_manager/event_router.cc b/chrome/browser/ash/extensions/file_manager/event_router.cc index 60a1af8b..19a34ef2 100644 --- a/chrome/browser/ash/extensions/file_manager/event_router.cc +++ b/chrome/browser/ash/extensions/file_manager/event_router.cc
@@ -1196,9 +1196,9 @@ // notifications for folders outside of those being watched by a file watcher. if (status.IsCompleted()) { std::set<std::pair<base::FilePath, url::Origin>> updated_paths; - if (status.destination_folder.is_valid()) { - updated_paths.emplace(status.destination_folder.virtual_path(), - status.destination_folder.origin()); + if (status.GetDestinationFolder().is_valid()) { + updated_paths.emplace(status.GetDestinationFolder().virtual_path(), + status.GetDestinationFolder().origin()); } for (const auto& source : status.sources) { updated_paths.emplace(source.url.virtual_path().DirName(), @@ -1219,6 +1219,7 @@ event_status.task_id = status.task_id; event_status.type = GetIOTaskType(status.type); event_status.state = GetIOTaskState(status.state); + event_status.destination_volume_id = status.GetDestinationVolumeId(); event_status.show_notification = status.show_notification; // Speedometer can produce infinite result which can't be serialized to JSON @@ -1227,9 +1228,9 @@ event_status.remaining_seconds = status.remaining_seconds; } - if (status.destination_folder.is_valid()) { + if (status.GetDestinationFolder().is_valid()) { event_status.destination_name = - util::GetDisplayablePath(profile_, status.destination_folder) + util::GetDisplayablePath(profile_, status.GetDestinationFolder()) .value_or(base::FilePath()) .BaseName() .value();
diff --git a/chrome/browser/ash/extensions/file_manager/system_notification_manager.cc b/chrome/browser/ash/extensions/file_manager/system_notification_manager.cc index 82e6b4b..8c23f96 100644 --- a/chrome/browser/ash/extensions/file_manager/system_notification_manager.cc +++ b/chrome/browser/ash/extensions/file_manager/system_notification_manager.cc
@@ -86,7 +86,7 @@ bool is_destination_drive = drive_integration_service && drive_integration_service->GetMountPointPath().IsParent( - status.destination_folder.path()); + status.GetDestinationFolder().path()); switch (status.type) { case OperationType::kCopy:
diff --git a/chrome/browser/ash/extensions/file_manager/system_notification_manager_unittest.cc b/chrome/browser/ash/extensions/file_manager/system_notification_manager_unittest.cc index f7857c4..4becb5b 100644 --- a/chrome/browser/ash/extensions/file_manager/system_notification_manager_unittest.cc +++ b/chrome/browser/ash/extensions/file_manager/system_notification_manager_unittest.cc
@@ -1077,7 +1077,7 @@ status.bytes_transferred = 0; status.sources.emplace_back(CreateTestFile("volume/src_file.txt"), absl::nullopt); - status.destination_folder = CreateTestFile("volume/dest_dir/"); + status.SetDestinationFolder(CreateTestFile("volume/dest_dir/")); // Send the copy begin/queued progress. auto* notification_manager = GetSystemNotificationManager(); @@ -1126,7 +1126,7 @@ status.bytes_transferred = 0; status.sources.emplace_back(CreateTestFile("volume/src_file.zip"), absl::nullopt); - status.destination_folder = CreateTestFile("volume/src_file/"); + status.SetDestinationFolder(CreateTestFile("volume/src_file/")); // Send the copy begin/queued progress. auto* notification_manager = GetSystemNotificationManager(); @@ -1176,7 +1176,7 @@ auto src = CreateTestFile("volume/src_file.txt"); status.sources.emplace_back(src, absl::nullopt); auto dst = CreateTestFile("volume/dest_dir/"); - status.destination_folder = dst; + status.SetDestinationFolder(dst); auto task = std::make_unique<file_manager::io_task::CopyOrMoveIOTask>( file_manager::io_task::OperationType::kCopy,
diff --git a/chrome/browser/ash/file_manager/copy_or_move_io_task.cc b/chrome/browser/ash/file_manager/copy_or_move_io_task.cc index 8bde9a5..87d81469 100644 --- a/chrome/browser/ash/file_manager/copy_or_move_io_task.cc +++ b/chrome/browser/ash/file_manager/copy_or_move_io_task.cc
@@ -36,7 +36,7 @@ DCHECK(type == OperationType::kCopy || type == OperationType::kMove); progress_.state = State::kQueued; progress_.type = type; - progress_.destination_folder = std::move(destination_folder); + progress_.SetDestinationFolder(std::move(destination_folder), profile); progress_.bytes_transferred = 0; progress_.total_bytes = 0; @@ -77,18 +77,18 @@ if (scanning_feature_enabled) { scanning_settings = enterprise_connectors::FileTransferAnalysisDelegate::IsEnabledVec( - profile_, source_urls_, progress_.destination_folder); + profile_, source_urls_, progress_.GetDestinationFolder()); } if (scanning_feature_enabled && !scanning_settings.empty()) { impl_ = std::make_unique<CopyOrMoveIOTaskScanningImpl>( progress_.type, progress_, std::move(destination_file_names_), - std::move(scanning_settings), progress_.destination_folder, profile_, - file_system_context_, progress_.show_notification); + std::move(scanning_settings), progress_.GetDestinationFolder(), + profile_, file_system_context_, progress_.show_notification); } else { impl_ = std::make_unique<CopyOrMoveIOTaskImpl>( progress_.type, progress_, std::move(destination_file_names_), - progress_.destination_folder, profile_, file_system_context_, + progress_.GetDestinationFolder(), profile_, file_system_context_, progress_.show_notification); }
diff --git a/chrome/browser/ash/file_manager/copy_or_move_io_task_impl.cc b/chrome/browser/ash/file_manager/copy_or_move_io_task_impl.cc index 1d0c774..f7f457f5 100644 --- a/chrome/browser/ash/file_manager/copy_or_move_io_task_impl.cc +++ b/chrome/browser/ash/file_manager/copy_or_move_io_task_impl.cc
@@ -227,7 +227,7 @@ DCHECK(idx < progress_.sources.size()); const base::FilePath& source = progress_.sources[idx].url.path(); - const base::FilePath& destination = progress_.destination_folder.path(); + const base::FilePath& destination = progress_.GetDestinationFolder().path(); constexpr auto metadata_fields = storage::FileSystemOperation::GET_METADATA_FIELD_IS_DIRECTORY | @@ -301,17 +301,18 @@ // Got file size for all files at this point! speedometer_.SetTotalBytes(progress_.total_bytes); - if (util::IsNonNativeFileSystemType(progress_.destination_folder.type())) { + if (util::IsNonNativeFileSystemType( + progress_.GetDestinationFolder().type())) { // Destination is a virtual filesystem, so skip checking free space. GenerateDestinationURL(0); } else { // For Drive, check we have enough local disk first, then check quota. - base::FilePath path = progress_.destination_folder.path(); + base::FilePath path = progress_.GetDestinationFolder().path(); auto* drive_integration_service = drive::util::GetIntegrationServiceByProfile(profile_); if (drive_integration_service && drive_integration_service->IsMounted() && drive_integration_service->GetMountPointPath().IsParent( - progress_.destination_folder.path())) { + progress_.GetDestinationFolder().path())) { path = drive_integration_service->GetDriveFsHost()->GetDataPath(); } base::ThreadPool::PostTaskAndReplyWithResult( @@ -329,8 +330,8 @@ bool is_drive = drive_integration_service && drive_integration_service->IsMounted() && drive_integration_service->GetMountPointPath().IsParent( - progress_.destination_folder.path()); - if (progress_.destination_folder.filesystem_id() == + progress_.GetDestinationFolder().path()); + if (progress_.GetDestinationFolder().filesystem_id() == util::GetDownloadsMountPointName(profile_) || is_drive) { free_space -= cryptohome::kMinFreeSpaceInBytes; @@ -342,14 +343,14 @@ if (progress_.type == OperationType::kMove) { for (size_t i = 0; i < source_sizes_.size(); i++) { if (!IsCrossFileSystem(profile_, progress_.sources[i].url, - progress_.destination_folder)) { + progress_.GetDestinationFolder())) { required_bytes -= source_sizes_[i]; } } } if (required_bytes > free_space) { - progress_.outputs.emplace_back(progress_.destination_folder, + progress_.outputs.emplace_back(progress_.GetDestinationFolder(), base::File::FILE_ERROR_NO_SPACE); LOG(ERROR) << "Insufficient free space in destination"; Complete(State::kError); @@ -358,7 +359,7 @@ if (is_drive) { bool is_shared_drive = drive_integration_service->IsSharedDrive( - progress_.destination_folder.path()); + progress_.GetDestinationFolder().path()); drive_integration_service->GetPooledQuotaUsage( base::BindOnce(base::BindOnce( &CopyOrMoveIOTaskImpl::GotDrivePooledQuota, @@ -389,7 +390,7 @@ !is_shared_drive && usage->total_user_bytes != -1 && (usage->total_user_bytes - usage->used_user_bytes) < required_bytes; if (org_exceeded || user_exceeded) { - progress_.outputs.emplace_back(progress_.destination_folder, + progress_.outputs.emplace_back(progress_.GetDestinationFolder(), base::File::FILE_ERROR_NO_SPACE); LOG(ERROR) << "Insufficient drive quota"; Complete(State::kError); @@ -403,7 +404,7 @@ if (is_shared_drive && drive_integration_service && drive_integration_service->IsMounted()) { drive_integration_service->GetMetadata( - progress_.destination_folder.path(), + progress_.GetDestinationFolder().path(), base::BindOnce(&CopyOrMoveIOTaskImpl::GotSharedDriveMetadata, weak_ptr_factory_.GetWeakPtr(), required_bytes)); return; @@ -426,7 +427,7 @@ const auto& quota = metadata->shared_drive_quota; if ((quota->individual_quota_bytes_total - quota->quota_bytes_used_in_drive) < required_bytes) { - progress_.outputs.emplace_back(progress_.destination_folder, + progress_.outputs.emplace_back(progress_.GetDestinationFolder(), base::File::FILE_ERROR_NO_SPACE); LOG(ERROR) << "Insufficient shared drive quota"; Complete(State::kError); @@ -450,7 +451,8 @@ : progress_.sources[idx].url.path().BaseName(); util::GenerateUnusedFilename( - progress_.destination_folder, destination_file_name, file_system_context_, + progress_.GetDestinationFolder(), destination_file_name, + file_system_context_, base::BindOnce(&CopyOrMoveIOTaskImpl::CopyOrMoveFile, weak_ptr_factory_.GetWeakPtr(), idx)); } @@ -462,7 +464,8 @@ DCHECK(idx < progress_.sources.size()); if (!destination_result.has_value()) { - progress_.outputs.emplace_back(progress_.destination_folder, absl::nullopt); + progress_.outputs.emplace_back(progress_.GetDestinationFolder(), + absl::nullopt); OnCopyOrMoveComplete(idx, destination_result.error()); return; } @@ -484,9 +487,9 @@ // as the parent directory. auto basename = source_url.path().BaseName(); auto replace_url = file_system_context_->CreateCrackedFileSystemURL( - progress_.destination_folder.storage_key(), - progress_.destination_folder.mount_type(), - progress_.destination_folder.virtual_path().Append( + progress_.GetDestinationFolder().storage_key(), + progress_.GetDestinationFolder().mount_type(), + progress_.GetDestinationFolder().virtual_path().Append( base::FilePath::FromUTF8Unsafe(basename.AsUTF8Unsafe()))); // If the source url and replace url are the same, the copy/move operation @@ -534,9 +537,9 @@ progress_.pause_params.conflict_is_directory = progress_.sources[idx].is_directory; auto destination_folder = file_system_context_->CreateCrackedFileSystemURL( - progress_.destination_folder.storage_key(), - progress_.destination_folder.mount_type(), - progress_.destination_folder.virtual_path()); + progress_.GetDestinationFolder().storage_key(), + progress_.GetDestinationFolder().mount_type(), + progress_.GetDestinationFolder().virtual_path()); progress_.pause_params.conflict_target_url = destination_folder.ToGURL().spec(); progress_callback_.Run(progress_);
diff --git a/chrome/browser/ash/file_manager/copy_or_move_io_task_scanning_impl.cc b/chrome/browser/ash/file_manager/copy_or_move_io_task_scanning_impl.cc index 7c7a553..a0313b5 100644 --- a/chrome/browser/ash/file_manager/copy_or_move_io_task_scanning_impl.cc +++ b/chrome/browser/ash/file_manager/copy_or_move_io_task_scanning_impl.cc
@@ -195,8 +195,9 @@ file_transfer_analysis_delegates_[idx] = enterprise_connectors::FileTransferAnalysisDelegate::Create( safe_browsing::DeepScanAccessPoint::FILE_TRANSFER, - progress_.sources[idx].url, progress_.destination_folder, profile_, - file_system_context_.get(), std::move(settings_[idx].value())); + progress_.sources[idx].url, progress_.GetDestinationFolder(), + profile_, file_system_context_.get(), + std::move(settings_[idx].value())); file_transfer_analysis_delegates_[idx]->UploadData( base::BindOnce(&CopyOrMoveIOTaskScanningImpl::MaybeScanForDisallowedFiles,
diff --git a/chrome/browser/ash/file_manager/copy_or_move_io_task_unittest.cc b/chrome/browser/ash/file_manager/copy_or_move_io_task_unittest.cc index a7b2636..c9d1423 100644 --- a/chrome/browser/ash/file_manager/copy_or_move_io_task_unittest.cc +++ b/chrome/browser/ash/file_manager/copy_or_move_io_task_unittest.cc
@@ -52,6 +52,7 @@ using ::testing::ElementsAreArray; using ::testing::Field; using ::testing::IsEmpty; +using ::testing::Property; using ::testing::Return; namespace file_manager { @@ -120,7 +121,7 @@ progress_.sources.emplace_back(CreateFileSystemURL("foo.txt"), absl::nullopt); base::CreateDirectory(temp_dir_.GetPath().Append("dest_folder")); - progress_.destination_folder = CreateFileSystemURL("dest_folder/"); + progress_.SetDestinationFolder(CreateFileSystemURL("dest_folder/")); CopyOrMoveIOTaskImpl task(GetParam(), progress_, {}, CreateFileSystemURL(""), &profile_, file_system_context_); @@ -136,7 +137,7 @@ progress_.sources.emplace_back(CreateFileSystemURL("foo.txt"), absl::nullopt); base::CreateDirectory(temp_dir_.GetPath().Append("dest_folder")); - progress_.destination_folder = CreateFileSystemURL("dest_folder/"); + progress_.SetDestinationFolder(CreateFileSystemURL("dest_folder/")); CopyOrMoveIOTaskImpl task(GetParam(), progress_, {}, CreateFileSystemURL(""), &profile_, file_system_context_); @@ -178,7 +179,7 @@ auto base_matcher = AllOf(Field(&ProgressStatus::type, GetParam()), Field(&ProgressStatus::sources, EntryStatusUrls(source_urls)), - Field(&ProgressStatus::destination_folder, dest), + Property(&ProgressStatus::GetDestinationFolder, dest), Field(&ProgressStatus::total_bytes, 2 * kTestFileSize)); base::MockRepeatingCallback<void(const ProgressStatus&)> progress_callback; base::MockOnceCallback<void(ProgressStatus)> complete_callback; @@ -259,7 +260,7 @@ auto base_matcher = AllOf(Field(&ProgressStatus::type, GetParam()), Field(&ProgressStatus::sources, EntryStatusUrls(source_urls)), - Field(&ProgressStatus::destination_folder, dest), + Property(&ProgressStatus::GetDestinationFolder, dest), Field(&ProgressStatus::total_bytes, 2 * kTestFileSize)); base::MockOnceCallback<void(ProgressStatus)> complete_callback; EXPECT_CALL( @@ -337,7 +338,7 @@ EXPECT_CALL( complete_callback, Run(AllOf(Field(&ProgressStatus::type, GetParam()), - Field(&ProgressStatus::destination_folder, dest), + Property(&ProgressStatus::GetDestinationFolder, dest), Field(&ProgressStatus::state, State::kError), Field(&ProgressStatus::bytes_transferred, 0), Field(&ProgressStatus::sources, EntryStatusUrls(source_urls)), @@ -380,7 +381,7 @@ EXPECT_CALL( complete_callback, Run(AllOf(Field(&ProgressStatus::type, GetParam()), - Field(&ProgressStatus::destination_folder, dest), + Property(&ProgressStatus::GetDestinationFolder, dest), Field(&ProgressStatus::state, State::kError), Field(&ProgressStatus::bytes_transferred, 2 * kTestFileSize), Field(&ProgressStatus::total_bytes, 2 * kTestFileSize), @@ -435,7 +436,7 @@ EXPECT_CALL( complete_callback, Run(AllOf(Field(&ProgressStatus::type, GetParam()), - Field(&ProgressStatus::destination_folder, dest), + Property(&ProgressStatus::GetDestinationFolder, dest), Field(&ProgressStatus::state, State::kSuccess), Field(&ProgressStatus::bytes_transferred, 2 * kTestFileSize), Field(&ProgressStatus::total_bytes, 2 * kTestFileSize), @@ -770,7 +771,7 @@ Field(&ProgressStatus::type, GetOperationType()), Field(&ProgressStatus::sources, EntryStatusUrls(GetSourceUrlsFromFileInfos(file_infos))), - Field(&ProgressStatus::destination_folder, dest), + Property(&ProgressStatus::GetDestinationFolder, dest), Field(&ProgressStatus::total_bytes, total_num_files * kTestFileSize)); } @@ -808,7 +809,7 @@ Field(&ProgressStatus::type, GetOperationType()), Field(&ProgressStatus::sources, EntryStatusUrls(GetSourceUrlsFromFileInfos(file_infos))), - Field(&ProgressStatus::destination_folder, dest), + Property(&ProgressStatus::GetDestinationFolder, dest), Field(&ProgressStatus::total_bytes, 0)))) .Times(num_calls); }
diff --git a/chrome/browser/ash/file_manager/extract_io_task.cc b/chrome/browser/ash/file_manager/extract_io_task.cc index 89c0ab9..3511d54f 100644 --- a/chrome/browser/ash/file_manager/extract_io_task.cc +++ b/chrome/browser/ash/file_manager/extract_io_task.cc
@@ -50,7 +50,7 @@ file_system_context_(std::move(file_system_context)) { progress_.type = OperationType::kExtract; progress_.state = State::kQueued; - progress_.destination_folder = parent_folder_; + progress_.SetDestinationFolder(parent_folder_, profile); progress_.bytes_transferred = 0; progress_.total_bytes = 0; // Store all the ZIP files in the selection so we have @@ -227,16 +227,16 @@ void ExtractIOTask::GotFreeDiskSpace(int64_t free_space) { auto* drive_integration_service = drive::util::GetIntegrationServiceByProfile(profile_); - if (progress_.destination_folder.filesystem_id() == + if (progress_.GetDestinationFolder().filesystem_id() == util::GetDownloadsMountPointName(profile_) || (drive_integration_service && drive_integration_service->GetMountPointPath().IsParent( - progress_.destination_folder.path()))) { + progress_.GetDestinationFolder().path()))) { free_space -= cryptohome::kMinFreeSpaceInBytes; } if (progress_.total_bytes > free_space) { - progress_.outputs.emplace_back(progress_.destination_folder, + progress_.outputs.emplace_back(progress_.GetDestinationFolder(), base::File::FILE_ERROR_NO_SPACE); progress_.state = State::kError; RecordUmaExtractStatus(ExtractStatus::kInsufficientDiskSpace);
diff --git a/chrome/browser/ash/file_manager/file_manager_jstest.cc b/chrome/browser/ash/file_manager/file_manager_jstest.cc index 346f7a2..1e1ad0b 100644 --- a/chrome/browser/ash/file_manager/file_manager_jstest.cc +++ b/chrome/browser/ash/file_manager/file_manager_jstest.cc
@@ -401,3 +401,7 @@ IN_PROC_BROWSER_TEST_F(FileManagerJsTest, DirectoryTreeContainer) { RunTestURL("containers/directory_tree_container_unittest.js"); } + +IN_PROC_BROWSER_TEST_F(FileManagerJsTest, EntryUtils) { + RunTestURL("common/js/entry_utils_unittest.js"); +}
diff --git a/chrome/browser/ash/file_manager/io_task.cc b/chrome/browser/ash/file_manager/io_task.cc index da888e43..97f637d 100644 --- a/chrome/browser/ash/file_manager/io_task.cc +++ b/chrome/browser/ash/file_manager/io_task.cc
@@ -10,6 +10,8 @@ #include "base/functional/callback.h" #include "base/task/sequenced_task_runner.h" #include "chrome/browser/ash/file_manager/path_util.h" +#include "chrome/browser/ash/file_manager/volume_manager.h" +#include "chrome/browser/profiles/profile.h" #include "storage/browser/file_system/file_system_url.h" namespace file_manager::io_task { @@ -54,6 +56,22 @@ .value(); } +void ProgressStatus::SetDestinationFolder(storage::FileSystemURL folder, + Profile* profile) { + destination_folder_ = std::move(folder); + if (!profile) { + return; + } + if (VolumeManager* const volume_manager = VolumeManager::Get(profile); + volume_manager) { + base::WeakPtr<Volume> volume = + volume_manager->FindVolumeFromPath(destination_folder_.path()); + if (volume) { + destination_volume_id_ = volume->volume_id(); + } + } +} + DummyIOTask::DummyIOTask(std::vector<storage::FileSystemURL> source_urls, storage::FileSystemURL destination_folder, OperationType type, @@ -61,7 +79,7 @@ : IOTask(show_notifications) { progress_.state = State::kQueued; progress_.type = type; - progress_.destination_folder = std::move(destination_folder); + progress_.SetDestinationFolder(std::move(destination_folder)); progress_.bytes_transferred = 0; progress_.total_bytes = 2;
diff --git a/chrome/browser/ash/file_manager/io_task.h b/chrome/browser/ash/file_manager/io_task.h index 7ab0c26..b514117 100644 --- a/chrome/browser/ash/file_manager/io_task.h +++ b/chrome/browser/ash/file_manager/io_task.h
@@ -112,7 +112,8 @@ }; // Represents the current progress of an I/O task. -struct ProgressStatus { +class ProgressStatus { + public: // Out-of-line constructors to appease the style linter. ProgressStatus(); ProgressStatus(const ProgressStatus& other) = delete; @@ -132,6 +133,14 @@ // Returns a default method for obtaining the source name. std::string GetSourceName(Profile* profile) const; + // Setter for the destination folder and the destination volume id. + void SetDestinationFolder(const storage::FileSystemURL folder, + Profile* profile = nullptr); + storage::FileSystemURL GetDestinationFolder() const { + return destination_folder_; + } + std::string GetDestinationVolumeId() const { return destination_volume_id_; } + // Task state. State state; @@ -148,10 +157,6 @@ // |sources|. std::vector<EntryStatus> outputs; - // Optional destination folder for operations that transfer files to a - // directory (e.g. copy or move). - storage::FileSystemURL destination_folder; - // I/O task state::PAUSED parameters. PauseParams pause_params; @@ -169,6 +174,15 @@ // Whether notifications should be shown on progress status. bool show_notification = true; + + private: + // Optional destination folder for operations that transfer files to a + // directory (e.g. copy or move). + storage::FileSystemURL destination_folder_; + + // Volume id of the destination directory for operations that transfer files + // to a directory (e.g. copy or move). + std::string destination_volume_id_; }; // An IOTask represents an I/O operation over multiple files, and is responsible
diff --git a/chrome/browser/ash/file_manager/io_task_controller_unittest.cc b/chrome/browser/ash/file_manager/io_task_controller_unittest.cc index 5c680ce4..9584e1f 100644 --- a/chrome/browser/ash/file_manager/io_task_controller_unittest.cc +++ b/chrome/browser/ash/file_manager/io_task_controller_unittest.cc
@@ -19,6 +19,7 @@ using testing::AllOf; using testing::Field; using testing::Invoke; +using testing::Property; namespace file_manager { namespace io_task { @@ -63,7 +64,7 @@ auto base_matcher = AllOf(Field(&ProgressStatus::type, OperationType::kCopy), Field(&ProgressStatus::sources, EntryStatusUrls(source_urls)), - Field(&ProgressStatus::destination_folder, dest)); + Property(&ProgressStatus::GetDestinationFolder, dest)); // The controller should synchronously send out a progress status when queued. EXPECT_CALL(observer, OnIOTaskStatus( @@ -115,7 +116,7 @@ auto base_matcher = AllOf(Field(&ProgressStatus::type, OperationType::kMove), Field(&ProgressStatus::sources, EntryStatusUrls(source_urls)), - Field(&ProgressStatus::destination_folder, dest)); + Property(&ProgressStatus::GetDestinationFolder, dest)); // The controller should synchronously send out a progress status when queued. EXPECT_CALL(observer, OnIOTaskStatus(
diff --git a/chrome/browser/ash/file_manager/restore_to_destination_io_task.cc b/chrome/browser/ash/file_manager/restore_to_destination_io_task.cc index 2381252a..3168a20 100644 --- a/chrome/browser/ash/file_manager/restore_to_destination_io_task.cc +++ b/chrome/browser/ash/file_manager/restore_to_destination_io_task.cc
@@ -45,7 +45,7 @@ base_path_(base_path) { progress_.state = State::kQueued; progress_.type = OperationType::kRestoreToDestination; - progress_.destination_folder = std::move(destination_folder); + progress_.SetDestinationFolder(std::move(destination_folder), profile); progress_.bytes_transferred = 0; progress_.total_bytes = 0; @@ -135,9 +135,8 @@ // parent task is tied to the life of the child task. move_io_task_ = std::make_unique<CopyOrMoveIOTask>( OperationType::kMove, std::move(source_urls_), - std::move(destination_file_names_), - std::move(progress_.destination_folder), profile_, - file_system_context_); + std::move(destination_file_names_), progress_.GetDestinationFolder(), + profile_, file_system_context_); // The existing callbacks need to be intercepted to ensure the IOTask // progress that is propagated is sent from the `RestoreToDestinationIOTask`
diff --git a/chrome/browser/ash/file_manager/zip_io_task.cc b/chrome/browser/ash/file_manager/zip_io_task.cc index 1012df4..7732186 100644 --- a/chrome/browser/ash/file_manager/zip_io_task.cc +++ b/chrome/browser/ash/file_manager/zip_io_task.cc
@@ -49,10 +49,11 @@ for (const base::FilePath& relative_path : src_files) { const base::FilePath absolute_path = src_dir.Append(relative_path); - if (base::GetFileInfo(absolute_path, &info)) + if (base::GetFileInfo(absolute_path, &info)) { total_bytes += info.is_directory ? base::ComputeDirectorySize(absolute_path) : info.size; + } } VLOG(1) << "<<< Total size is " << total_bytes << " bytes"; return total_bytes; @@ -71,7 +72,7 @@ file_system_context_(file_system_context) { progress_.state = State::kQueued; progress_.type = OperationType::kZip; - progress_.destination_folder = std::move(parent_folder); + progress_.SetDestinationFolder(std::move(parent_folder), profile); progress_.bytes_transferred = 0; progress_.total_bytes = 0; @@ -100,10 +101,10 @@ progress_.state = State::kInProgress; // Convert the destination folder URL to absolute path. - source_dir_ = progress_.destination_folder.path(); - if (!ash::FileSystemBackend::CanHandleURL(progress_.destination_folder) || + source_dir_ = progress_.GetDestinationFolder().path(); + if (!ash::FileSystemBackend::CanHandleURL(progress_.GetDestinationFolder()) || source_dir_.empty()) { - progress_.outputs.emplace_back(progress_.destination_folder, + progress_.outputs.emplace_back(progress_.GetDestinationFolder(), base::File::FILE_ERROR_NOT_FOUND); Complete(State::kError); return; @@ -188,7 +189,7 @@ zip_name = source_relative_paths_[0].BaseName().ReplaceExtension("zip"); } util::GenerateUnusedFilename( - progress_.destination_folder, zip_name, file_system_context_, + progress_.GetDestinationFolder(), zip_name, file_system_context_, base::BindOnce(&ZipIOTask::ZipItems, weak_ptr_factory_.GetWeakPtr())); } @@ -196,7 +197,7 @@ void ZipIOTask::ZipItems( base::FileErrorOr<storage::FileSystemURL> destination_result) { if (!destination_result.has_value()) { - progress_.outputs.emplace_back(progress_.destination_folder, + progress_.outputs.emplace_back(progress_.GetDestinationFolder(), destination_result.error()); Complete(State::kError); return;
diff --git a/chrome/browser/ash/guest_os/public/guest_os_wayland_server.cc b/chrome/browser/ash/guest_os/public/guest_os_wayland_server.cc index 1bf987b..240140b 100644 --- a/chrome/browser/ash/guest_os/public/guest_os_wayland_server.cc +++ b/chrome/browser/ash/guest_os/public/guest_os_wayland_server.cc
@@ -114,8 +114,9 @@ GuestOsWaylandServer::ServerDetails::~ServerDetails() { // In tests, this is used to avoid dealing with the real server controller. - if (server_path_.empty()) + if (server_path_.empty()) { return; + } GuestOsSecurityDelegate::MaybeRemoveServer(security_delegate_, server_path_); } @@ -136,6 +137,21 @@ base::BindOnce(&OnWaylandServerStarted, std::move(response_callback))); } +// static +void GuestOsWaylandServer::ListenOnSocket( + const vm_tools::wl::ListenOnSocketRequest& request, + base::ScopedFD socket_fd, + base::OnceCallback<void(absl::optional<std::string>)> response_callback) { + std::move(response_callback).Run({"ListenOnSocket not implemented."}); +} + +// static +void GuestOsWaylandServer::CloseSocket( + const vm_tools::wl::CloseSocketRequest& request, + base::OnceCallback<void(absl::optional<std::string>)> response_callback) { + std::move(response_callback).Run({"CloseSocket not implemented."}); +} + GuestOsWaylandServer::GuestOsWaylandServer(Profile* profile) : profile_(profile) { delegate_holders_[vm_tools::launch::BOREALIS] =
diff --git a/chrome/browser/ash/guest_os/public/guest_os_wayland_server.h b/chrome/browser/ash/guest_os/public/guest_os_wayland_server.h index c184e71..43580fb 100644 --- a/chrome/browser/ash/guest_os/public/guest_os_wayland_server.h +++ b/chrome/browser/ash/guest_os/public/guest_os_wayland_server.h
@@ -9,10 +9,13 @@ #include "base/containers/flat_map.h" #include "base/files/file_path.h" +#include "base/files/scoped_file.h" #include "base/functional/callback_forward.h" #include "base/memory/weak_ptr.h" #include "chrome/browser/ash/borealis/infra/expected.h" #include "chromeos/ash/components/dbus/vm_launch/launch.pb.h" +#include "chromeos/ash/components/dbus/vm_wl/wl.pb.h" +#include "third_party/abseil-cpp/absl/types/optional.h" class Profile; @@ -70,12 +73,29 @@ // Creates a wayland server as per the |request|, and responds with the // relevant details in the |response_callback|. This API is used by e.g. // dbus. + // + // TODO(b/270254359): deprecate this method. static void StartServer( const vm_tools::launch::StartWaylandServerRequest& request, base::OnceCallback< void(borealis::Expected<vm_tools::launch::StartWaylandServerResponse, std::string>)> response_callback); + // Use the given |socket_fd| as a wayland socket for the VM given by + // |request|. Invokes the |response_callback| with nullopt on success, or a + // string description of an error on failure. + static void ListenOnSocket( + const vm_tools::wl::ListenOnSocketRequest& request, + base::ScopedFD socket_fd, + base::OnceCallback<void(absl::optional<std::string>)> response_callback); + + // Advise that the wayland server for the VM given in |request| is no-longer + // needed. Invokes the |response_callback| with nullopt on success, or a + // string description of an error on failure. + static void CloseSocket( + const vm_tools::wl::CloseSocketRequest& request, + base::OnceCallback<void(absl::optional<std::string>)> response_callback); + explicit GuestOsWaylandServer(Profile* profile); ~GuestOsWaylandServer();
diff --git a/chrome/browser/ash/policy/reporting/metrics_reporting/apps/app_usage_telemetry_sampler.cc b/chrome/browser/ash/policy/reporting/metrics_reporting/apps/app_usage_telemetry_sampler.cc new file mode 100644 index 0000000..e7cbdea --- /dev/null +++ b/chrome/browser/ash/policy/reporting/metrics_reporting/apps/app_usage_telemetry_sampler.cc
@@ -0,0 +1,119 @@ +// 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/ash/policy/reporting/metrics_reporting/apps/app_usage_telemetry_sampler.h" + +#include <memory> + +#include "base/time/time.h" +#include "chrome/browser/apps/app_service/metrics/app_platform_metrics.h" +#include "chrome/browser/apps/app_service/metrics/app_platform_metrics_utils.h" +#include "chrome/browser/ash/profiles/profile_helper.h" +#include "chrome/browser/profiles/profile.h" +#include "components/prefs/pref_service.h" +#include "components/prefs/scoped_user_pref_update.h" +#include "components/reporting/metrics/sampler.h" +#include "components/reporting/proto/synced/metric_data.pb.h" +#include "components/services/app_service/public/cpp/app_types.h" +#include "components/user_manager/user.h" +#include "components/user_manager/user_manager.h" +#include "content/public/browser/browser_task_traits.h" +#include "content/public/browser/browser_thread.h" +#include "third_party/abseil-cpp/absl/types/optional.h" + +namespace reporting { +namespace { + +// Returns the primary user profile. We use this every time we need to access +// the profile so we can prevent dangling pointer references. +Profile* GetPrimaryUserProfile() { + const ::user_manager::User* const primary_user = + ::user_manager::UserManager::Get()->GetPrimaryUser(); + DCHECK(primary_user); + DCHECK(primary_user->is_profile_created()); + auto* const profile = + ::ash::ProfileHelper::Get()->GetProfileByUser(primary_user); + DCHECK(profile); + return profile; +} + +} // namespace + +AppUsageTelemetrySampler::AppUsageTelemetrySampler() = default; + +AppUsageTelemetrySampler::~AppUsageTelemetrySampler() = default; + +void AppUsageTelemetrySampler::MaybeCollect(OptionalMetricCallback callback) { + if (!::content::BrowserThread::CurrentlyOn(::content::BrowserThread::UI)) { + ::content::GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, + base::BindOnce(&AppUsageTelemetrySampler::MaybeCollect, + weak_ptr_factory_.GetWeakPtr(), std::move(callback))); + return; + } + auto* const profile = GetPrimaryUserProfile(); + MetricData metric_data; + auto* const app_usage_data = metric_data.mutable_telemetry_data() + ->mutable_app_telemetry() + ->mutable_app_usage_data(); + const PrefService* const user_prefs = profile->GetPrefs(); + if (!user_prefs->HasPrefPath(::apps::kAppUsageTime)) { + // No usage data in the pref store. + std::move(callback).Run(absl::nullopt); + return; + } + + // Parse app instance usage from the pref store and populate `app_usage_data`. + for (auto usage_it : user_prefs->GetDict(::apps::kAppUsageTime)) { + ::apps::AppPlatformMetrics::UsageTime usage_time(usage_it.second); + if (usage_time.reporting_usage_time.is_zero()) { + // No reporting usage tracked by the `AppUsageCollector` since it was last + // enabled, so we skip. The `AppPlatformMetrics` component will + // subsequently delete this entry once it reports its UKM snapshot. + continue; + } + + ::apps::AppType app_type = ::apps::GetAppType(profile, usage_time.app_id); + AppUsageData::AppUsage* const app_usage = + app_usage_data->mutable_app_usage()->Add(); + app_usage->set_app_instance_id(usage_it.first); + app_usage->set_app_id(usage_time.app_id); + app_usage->set_app_type( + ::apps::ConvertAppTypeToProtoApplicationType(app_type)); + app_usage->set_running_time_ms( + usage_time.reporting_usage_time.InMilliseconds()); + } + + if (app_usage_data->app_usage().empty()) { + // No app instance usage to report. + std::move(callback).Run(absl::nullopt); + return; + } + + std::move(callback).Run(metric_data); + ResetAppUsageDataInPrefStore(app_usage_data); +} + +void AppUsageTelemetrySampler::ResetAppUsageDataInPrefStore( + const AppUsageData* app_usage_data) { + DCHECK_CURRENTLY_ON(::content::BrowserThread::UI); + auto* const profile = GetPrimaryUserProfile(); + ScopedDictPrefUpdate usage_dict_pref(profile->GetPrefs(), + ::apps::kAppUsageTime); + for (const auto& usage_info : app_usage_data->app_usage()) { + const std::string& instance_id = usage_info.app_instance_id(); + DCHECK(usage_dict_pref->contains(instance_id)) + << "Missing app usage data for instance: " << instance_id; + + // Reduce usage time tracked in the pref store based on the data that was + // reported. + const auto running_time = base::Milliseconds(usage_info.running_time_ms()); + ::apps::AppPlatformMetrics::UsageTime usage_time( + *usage_dict_pref->FindByDottedPath(instance_id)); + usage_time.reporting_usage_time -= running_time; + usage_dict_pref->SetByDottedPath(instance_id, usage_time.ConvertToDict()); + } +} + +} // namespace reporting
diff --git a/chrome/browser/ash/policy/reporting/metrics_reporting/apps/app_usage_telemetry_sampler.h b/chrome/browser/ash/policy/reporting/metrics_reporting/apps/app_usage_telemetry_sampler.h new file mode 100644 index 0000000..001f9d8 --- /dev/null +++ b/chrome/browser/ash/policy/reporting/metrics_reporting/apps/app_usage_telemetry_sampler.h
@@ -0,0 +1,43 @@ +// 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_ASH_POLICY_REPORTING_METRICS_REPORTING_APPS_APP_USAGE_TELEMETRY_SAMPLER_H_ +#define CHROME_BROWSER_ASH_POLICY_REPORTING_METRICS_REPORTING_APPS_APP_USAGE_TELEMETRY_SAMPLER_H_ + +#include "base/memory/raw_ptr.h" +#include "base/memory/weak_ptr.h" +#include "chrome/browser/profiles/profile.h" +#include "components/reporting/metrics/sampler.h" +#include "components/reporting/proto/synced/metric_data.pb.h" + +namespace reporting { + +// Sampler used to collect app usage telemetry from the primary user profile +// pref store. This usage data is originally persisted in the pref store by the +// `AppUsageCollector` as it observes and tracks data collection from the +// `AppPlatformMetrics` component. +class AppUsageTelemetrySampler : public Sampler { + public: + AppUsageTelemetrySampler(); + AppUsageTelemetrySampler(const AppUsageTelemetrySampler& other) = delete; + AppUsageTelemetrySampler& operator=(const AppUsageTelemetrySampler& other) = + delete; + ~AppUsageTelemetrySampler() override; + + // Collects apps usage telemetry data from the user pref store across several + // instances and triggers the specified callback with batched usage data. + // Sampler: + void MaybeCollect(OptionalMetricCallback callback) override; + + private: + // Resets app usage entries in the pref store by discounting reported usage + // time. Triggered only after the data has been consumed to avoid data loss. + void ResetAppUsageDataInPrefStore(const AppUsageData* app_usage_data); + + base::WeakPtrFactory<AppUsageTelemetrySampler> weak_ptr_factory_{this}; +}; + +} // namespace reporting + +#endif // CHROME_BROWSER_ASH_POLICY_REPORTING_METRICS_REPORTING_APPS_APP_USAGE_TELEMETRY_SAMPLER_H_
diff --git a/chrome/browser/ash/policy/reporting/metrics_reporting/apps/app_usage_telemetry_sampler_unittest.cc b/chrome/browser/ash/policy/reporting/metrics_reporting/apps/app_usage_telemetry_sampler_unittest.cc new file mode 100644 index 0000000..d716f79 --- /dev/null +++ b/chrome/browser/ash/policy/reporting/metrics_reporting/apps/app_usage_telemetry_sampler_unittest.cc
@@ -0,0 +1,288 @@ +// 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/ash/policy/reporting/metrics_reporting/apps/app_usage_telemetry_sampler.h" + +#include <memory> +#include <string> +#include <tuple> + +#include "base/json/values_util.h" +#include "base/memory/ptr_util.h" +#include "base/memory/raw_ptr.h" +#include "base/time/time.h" +#include "base/unguessable_token.h" +#include "base/values.h" +#include "chrome/browser/apps/app_service/metrics/app_platform_metrics.h" +#include "chrome/browser/ash/login/users/fake_chrome_user_manager.h" +#include "chrome/browser/ash/profiles/profile_helper.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/test/base/testing_profile.h" +#include "components/prefs/pref_service.h" +#include "components/prefs/scoped_user_pref_update.h" +#include "components/reporting/proto/synced/metric_data.pb.h" +#include "components/reporting/util/test_support_callbacks.h" +#include "components/services/app_service/public/cpp/app_types.h" +#include "components/services/app_service/public/protos/app_types.pb.h" +#include "components/user_manager/scoped_user_manager.h" +#include "components/user_manager/user_manager.h" +#include "content/public/test/browser_task_environment.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/abseil-cpp/absl/types/optional.h" + +using ::testing::_; +using ::testing::Eq; +using ::testing::NotNull; +using ::testing::Pointwise; +using ::testing::StrEq; +using ::testing::UnorderedPointwise; + +namespace reporting { +namespace { + +constexpr char kTestUserEmail[] = "test@test.com"; +constexpr char kTestAppId[] = "TestApp"; + +// Checks equality of the two protos in an std::tuple. Useful for matching two +// two protos using ::testing::Pointwise or ::testing::UnorderedPointwise. +MATCHER(EqualsProto, "") { + std::string serialized1, serialized2; + std::get<0>(arg).SerializeToString(&serialized1); + std::get<1>(arg).SerializeToString(&serialized2); + return serialized1 == serialized2; +} + +class AppUsageTelemetrySamplerTest : public ::testing::Test { + protected: + void SetUp() override { + // Set up user manager and test profile. + fake_user_manager_ = new ::ash::FakeChromeUserManager(); + scoped_user_manager_ = std::make_unique<::user_manager::ScopedUserManager>( + base::WrapUnique(fake_user_manager_.get())); + AccountId account_id = AccountId::FromUserEmail(kTestUserEmail); + const ::user_manager::User* const user = + fake_user_manager_->AddUser(account_id); + fake_user_manager_->UserLoggedIn(account_id, user->username_hash(), + /*browser_restart=*/false, + /*is_child=*/false); + fake_user_manager_->SimulateUserProfileLoad(account_id); + profile_ = std::make_unique<TestingProfile>(); + ::ash::ProfileHelper::Get()->SetUserToProfileMappingForTesting( + user, profile_.get()); + } + + // Simulates app usage for the specified app usage duration by aggregating + // relevant usage info in the pref store. + void CreateOrUpdateAppUsageForInstance( + const base::UnguessableToken& instance_id, + const base::TimeDelta& usage_duration) { + PrefService* const user_prefs = profile_->GetPrefs(); + if (!user_prefs->HasPrefPath(::apps::kAppUsageTime)) { + // Create empty dictionary if none exists in the pref store. + user_prefs->SetDict(::apps::kAppUsageTime, base::Value::Dict()); + } + + ScopedDictPrefUpdate usage_dict_pref(profile_->GetPrefs(), + ::apps::kAppUsageTime); + const auto& instance_id_string = instance_id.ToString(); + if (!usage_dict_pref->contains(instance_id_string)) { + // Create a new entry in the pref store with the specified running time. + ::apps::AppPlatformMetrics::UsageTime usage_time; + usage_time.app_id = kTestAppId; + usage_time.reporting_usage_time = usage_duration; + usage_dict_pref->SetByDottedPath(instance_id_string, + usage_time.ConvertToDict()); + return; + } + + // Aggregate and update just the running time otherwise. + ::apps::AppPlatformMetrics::UsageTime usage_time( + *usage_dict_pref->FindByDottedPath(instance_id_string)); + usage_time.reporting_usage_time += usage_duration; + usage_dict_pref->SetByDottedPath(instance_id_string, + usage_time.ConvertToDict()); + } + + void VerifyAppUsageDataInPrefStoreForInstance( + const base::UnguessableToken& instance_id, + const base::TimeDelta& expected_usage_time) { + const auto& usage_dict_pref = + profile_->GetPrefs()->GetDict(::apps::kAppUsageTime); + const auto& instance_id_string = instance_id.ToString(); + ASSERT_THAT(usage_dict_pref.Find(instance_id_string), NotNull()); + EXPECT_THAT(*usage_dict_pref.Find(instance_id_string) + ->FindStringKey(::apps::kUsageTimeAppIdKey), + StrEq(kTestAppId)); + EXPECT_THAT(base::ValueToTimeDelta( + usage_dict_pref.FindDict(instance_id_string) + ->Find(::apps::kReportingUsageTimeDurationKey)), + Eq(expected_usage_time)); + } + + // Returns an `AppUsageData::AppUsage` proto message that tests can use to + // test match with the actual one. + const AppUsageData::AppUsage AppUsageProto( + const base::UnguessableToken& instance_id, + const base::TimeDelta& running_time) const { + AppUsageData::AppUsage app_usage; + app_usage.set_app_id(kTestAppId); + app_usage.set_app_type(::apps::ApplicationType::APPLICATION_TYPE_UNKNOWN); + app_usage.set_app_instance_id(instance_id.ToString()); + app_usage.set_running_time_ms(running_time.InMilliseconds()); + return app_usage; + } + + content::BrowserTaskEnvironment task_environment_; + + std::unique_ptr<TestingProfile> profile_; + AppUsageTelemetrySampler app_usage_telemetry_sampler_; + + private: + raw_ptr<::ash::FakeChromeUserManager> fake_user_manager_; + std::unique_ptr<::user_manager::ScopedUserManager> scoped_user_manager_; +}; + +TEST_F(AppUsageTelemetrySamplerTest, CollectAppUsageDataForInstance) { + // Simulate app usage so we have data in the pref store to work with. + static constexpr base::TimeDelta kAppUsageDuration = base::Minutes(2); + const base::UnguessableToken& kInstanceId = base::UnguessableToken::Create(); + CreateOrUpdateAppUsageForInstance(kInstanceId, kAppUsageDuration); + ASSERT_THAT(profile_->GetPrefs()->GetDict(::apps::kAppUsageTime).size(), + Eq(1UL)); + + // Attempt to collect this data and verify reported data. + test::TestEvent<absl::optional<MetricData>> test_event; + app_usage_telemetry_sampler_.MaybeCollect(test_event.cb()); + const absl::optional<MetricData> metric_data_result = test_event.result(); + ASSERT_TRUE(metric_data_result.has_value()); + const MetricData& metric_data = metric_data_result.value(); + ASSERT_TRUE(metric_data.has_telemetry_data()); + ASSERT_TRUE(metric_data.telemetry_data().has_app_telemetry()); + ASSERT_TRUE( + metric_data.telemetry_data().app_telemetry().has_app_usage_data()); + EXPECT_THAT( + metric_data.telemetry_data().app_telemetry().app_usage_data().app_usage(), + Pointwise(EqualsProto(), + {AppUsageProto(kInstanceId, kAppUsageDuration)})); + + // Also verify usage data is reset in the pref store. + VerifyAppUsageDataInPrefStoreForInstance(kInstanceId, base::TimeDelta()); +} + +TEST_F(AppUsageTelemetrySamplerTest, NoAppUsageData) { + test::TestEvent<absl::optional<MetricData>> test_event; + app_usage_telemetry_sampler_.MaybeCollect(test_event.cb()); + const absl::optional<MetricData> metric_data_result = test_event.result(); + ASSERT_FALSE(metric_data_result.has_value()); +} + +TEST_F(AppUsageTelemetrySamplerTest, CollectResetAppUsageData) { + // Simulate app usage so we have data in the pref store to work with. + static constexpr base::TimeDelta kAppUsageDuration = base::Minutes(2); + const base::UnguessableToken& kInstanceId = base::UnguessableToken::Create(); + CreateOrUpdateAppUsageForInstance(kInstanceId, kAppUsageDuration); + ASSERT_THAT(profile_->GetPrefs()->GetDict(::apps::kAppUsageTime).size(), + Eq(1UL)); + VerifyAppUsageDataInPrefStoreForInstance(kInstanceId, kAppUsageDuration); + + // Attempt to collect this data and verify data is reset after it is reported. + { + test::TestEvent<absl::optional<MetricData>> test_event; + app_usage_telemetry_sampler_.MaybeCollect(test_event.cb()); + const absl::optional<MetricData> metric_data_result = test_event.result(); + ASSERT_TRUE(metric_data_result.has_value()); + VerifyAppUsageDataInPrefStoreForInstance(kInstanceId, base::TimeDelta()); + } + + // Attempt to collect data after it was reset in the previous step and verify + // nothing is reported. + { + test::TestEvent<absl::optional<MetricData>> test_event; + app_usage_telemetry_sampler_.MaybeCollect(test_event.cb()); + const absl::optional<MetricData> metric_data_result = test_event.result(); + ASSERT_FALSE(metric_data_result.has_value()); + } +} + +TEST_F(AppUsageTelemetrySamplerTest, CollectSubsequentAppUsageData) { + // Simulate app usage so we have data in the pref store to work with. + static constexpr base::TimeDelta kAppUsageDuration = base::Minutes(2); + const base::UnguessableToken& kInstanceId = base::UnguessableToken::Create(); + CreateOrUpdateAppUsageForInstance(kInstanceId, kAppUsageDuration); + ASSERT_THAT(profile_->GetPrefs()->GetDict(::apps::kAppUsageTime).size(), + Eq(1UL)); + VerifyAppUsageDataInPrefStoreForInstance(kInstanceId, kAppUsageDuration); + + // Attempt to collect this data and verify data is reset after it is reported. + { + test::TestEvent<absl::optional<MetricData>> test_event; + app_usage_telemetry_sampler_.MaybeCollect(test_event.cb()); + const absl::optional<MetricData> metric_data_result = test_event.result(); + ASSERT_TRUE(metric_data_result.has_value()); + VerifyAppUsageDataInPrefStoreForInstance(kInstanceId, base::TimeDelta()); + } + + // Simulate additional usage after the previous collection. + CreateOrUpdateAppUsageForInstance(kInstanceId, kAppUsageDuration); + + // Attempt to collect data and verify only data tracked from previous + // collection is reported. + { + test::TestEvent<absl::optional<MetricData>> test_event; + app_usage_telemetry_sampler_.MaybeCollect(test_event.cb()); + const absl::optional<MetricData> metric_data_result = test_event.result(); + ASSERT_TRUE(metric_data_result.has_value()); + const MetricData& metric_data = metric_data_result.value(); + ASSERT_TRUE(metric_data.has_telemetry_data()); + ASSERT_TRUE(metric_data.telemetry_data().has_app_telemetry()); + ASSERT_TRUE( + metric_data.telemetry_data().app_telemetry().has_app_usage_data()); + EXPECT_THAT(metric_data.telemetry_data() + .app_telemetry() + .app_usage_data() + .app_usage(), + Pointwise(EqualsProto(), + {AppUsageProto(kInstanceId, kAppUsageDuration)})); + VerifyAppUsageDataInPrefStoreForInstance(kInstanceId, base::TimeDelta()); + } +} + +TEST_F(AppUsageTelemetrySamplerTest, + CollectAppUsageDataAcrossMultipleInstances) { + // Simulate app usage across instances so we have data in the pref store to + // work with. + static constexpr base::TimeDelta kAppUsageDuration = base::Minutes(2); + const base::UnguessableToken& kInstanceId1 = base::UnguessableToken::Create(); + const base::UnguessableToken& kInstanceId2 = base::UnguessableToken::Create(); + CreateOrUpdateAppUsageForInstance(kInstanceId1, kAppUsageDuration); + CreateOrUpdateAppUsageForInstance(kInstanceId2, kAppUsageDuration); + ASSERT_THAT(profile_->GetPrefs()->GetDict(::apps::kAppUsageTime).size(), + Eq(2UL)); + VerifyAppUsageDataInPrefStoreForInstance(kInstanceId1, kAppUsageDuration); + VerifyAppUsageDataInPrefStoreForInstance(kInstanceId2, kAppUsageDuration); + + // Attempt to collect usage data and verify data being reported. + test::TestEvent<absl::optional<MetricData>> test_event; + app_usage_telemetry_sampler_.MaybeCollect(test_event.cb()); + const absl::optional<MetricData> metric_data_result = test_event.result(); + ASSERT_TRUE(metric_data_result.has_value()); + const MetricData& metric_data = metric_data_result.value(); + ASSERT_TRUE(metric_data.has_telemetry_data()); + ASSERT_TRUE(metric_data.telemetry_data().has_app_telemetry()); + ASSERT_TRUE( + metric_data.telemetry_data().app_telemetry().has_app_usage_data()); + EXPECT_THAT( + metric_data.telemetry_data().app_telemetry().app_usage_data().app_usage(), + UnorderedPointwise(EqualsProto(), + {AppUsageProto(kInstanceId1, kAppUsageDuration), + AppUsageProto(kInstanceId2, kAppUsageDuration)})); + + // Verify data is reset in the pref store now that it has been reported. + VerifyAppUsageDataInPrefStoreForInstance(kInstanceId1, base::TimeDelta()); + VerifyAppUsageDataInPrefStoreForInstance(kInstanceId2, base::TimeDelta()); +} + +} // namespace +} // namespace reporting
diff --git a/chrome/browser/ash/video_conference/BUILD.gn b/chrome/browser/ash/video_conference/BUILD.gn index c7530cbe..67bd7df 100644 --- a/chrome/browser/ash/video_conference/BUILD.gn +++ b/chrome/browser/ash/video_conference/BUILD.gn
@@ -20,17 +20,3 @@ "//components/prefs", ] } - -source_set("unit_tests") { - testonly = true - sources = [ "video_conference_manager_ash_unittest.cc" ] - deps = [ - ":video_conference", - "//ash", - "//base", - "//base/test:test_support", - "//chromeos/crosapi/mojom", - "//testing/gmock", - "//testing/gtest", - ] -}
diff --git a/chrome/browser/ash/video_conference/video_conference_app_service_client.cc b/chrome/browser/ash/video_conference/video_conference_app_service_client.cc index 089e3c2..fab9236 100644 --- a/chrome/browser/ash/video_conference/video_conference_app_service_client.cc +++ b/chrome/browser/ash/video_conference/video_conference_app_service_client.cc
@@ -205,6 +205,11 @@ const apps::InstanceUpdate& update) { const AppIdString& app_id = update.AppId(); + // We only care about the apps being tracked already. + if (!base::Contains(id_to_app_state_, app_id)) { + return; + } + // An instance of app_id is about to be destructed. if (update.IsDestruction() && instance_registry_->GetInstances(app_id).size() <= 1) { @@ -218,8 +223,7 @@ } if (update.StateChanged() && - update.State() == apps::InstanceState::kVisible && - base::Contains(id_to_app_state_, app_id)) { + (update.State() & apps::InstanceState::kActive) != 0) { id_to_app_state_[app_id].last_activity_time = update.LastUpdatedTime(); return; }
diff --git a/chrome/browser/ash/video_conference/video_conference_app_service_client_browsertest.cc b/chrome/browser/ash/video_conference/video_conference_app_service_client_browsertest.cc index af82148..42f77f26 100644 --- a/chrome/browser/ash/video_conference/video_conference_app_service_client_browsertest.cc +++ b/chrome/browser/ash/video_conference/video_conference_app_service_client_browsertest.cc
@@ -112,7 +112,7 @@ // Ideally, the following should be automatically triggered by showing the // window_; but that is not the case for now. auto instance = instance_->Clone(); - instance->UpdateState(apps::InstanceState::kVisible, base::Time::Now()); + instance->UpdateState(apps::InstanceState::kActive, base::Time::Now()); instance_registry_->OnInstance(std::move(instance)); }
diff --git a/chrome/browser/ash/video_conference/video_conference_manager_ash.cc b/chrome/browser/ash/video_conference/video_conference_manager_ash.cc index 69e99a99..410da4f 100644 --- a/chrome/browser/ash/video_conference/video_conference_manager_ash.cc +++ b/chrome/browser/ash/video_conference/video_conference_manager_ash.cc
@@ -65,6 +65,14 @@ } } + // Sort all apps based on last activity time. + std::sort( + apps.begin(), apps.end(), + [](const crosapi::mojom::VideoConferenceMediaAppInfoPtr& app1, + const crosapi::mojom::VideoConferenceMediaAppInfoPtr& app2) { + return app1->last_activity_time > app2->last_activity_time; + }); + // Call bound |ui_callback| with aggregated app info structs. std::move(callback).Run(std::move(apps)); },
diff --git a/chrome/browser/ash/video_conference/video_conference_manager_ash_unittest.cc b/chrome/browser/ash/video_conference/video_conference_manager_ash_unittest.cc index d671ff5..036c349 100644 --- a/chrome/browser/ash/video_conference/video_conference_manager_ash_unittest.cc +++ b/chrome/browser/ash/video_conference/video_conference_manager_ash_unittest.cc
@@ -103,13 +103,16 @@ // Tests |VideoConferenceManagerAsh::GetMediaApps| returns correct aggregated // results from all VcClients. TEST_F(VideoConferenceManagerAshTest, VcManagerGetMediaApps) { + const auto now = base::Time::Now(); + const auto duration = base::Seconds(1); + std::unique_ptr<FakeVcManagerCppClient> client1 = std::make_unique<FakeVcManagerCppClient>(vc_manager()); client1->apps_.push_back(crosapi::mojom::VideoConferenceMediaAppInfo::New( /*id=*/base::UnguessableToken::Create(), - /*last_activity_time=*/base::Time::Now(), + /*last_activity_time=*/now, /*is_capturing_camera=*/false, /*is_capturing_microphone=*/false, - /*is_capturing_screen=*/true, /*title=*/u"Test App", + /*is_capturing_screen=*/true, /*title=*/u"Test App0", /*url=*/absl::nullopt)); vc_manager().RegisterCppClient(client1.get(), client1->id_); @@ -119,6 +122,7 @@ vc_manager().GetMediaApps(base::BindLambdaForTesting( [&](VideoConferenceManagerAsh::MediaApps apps) { EXPECT_EQ(apps.size(), 1u); + EXPECT_EQ(apps[0]->title, u"Test App0"); auto status = GetAggregatedCaptureStatus(std::move(apps)); @@ -131,15 +135,17 @@ client1->apps_.push_back(crosapi::mojom::VideoConferenceMediaAppInfo::New( /*id=*/base::UnguessableToken::Create(), - /*last_activity_time=*/base::Time::Now(), + /*last_activity_time=*/now + duration * 10, /*is_capturing_camera=*/true, /*is_capturing_microphone=*/false, - /*is_capturing_screen=*/true, /*title=*/u"Test App", + /*is_capturing_screen=*/true, /*title=*/u"Test App1", /*url=*/absl::nullopt)); base::RunLoop run_loop2; vc_manager().GetMediaApps(base::BindLambdaForTesting( [&](VideoConferenceManagerAsh::MediaApps apps) { EXPECT_EQ(apps.size(), 2UL); + EXPECT_EQ(apps[0]->title, u"Test App1"); + EXPECT_EQ(apps[1]->title, u"Test App0"); auto status = GetAggregatedCaptureStatus(std::move(apps)); @@ -156,9 +162,9 @@ std::make_unique<FakeVcManagerCppClient>(vc_manager()); client2->apps_.push_back(crosapi::mojom::VideoConferenceMediaAppInfo::New( /*id=*/base::UnguessableToken::Create(), - /*last_activity_time=*/base::Time::Now(), + /*last_activity_time=*/now + duration * 2, /*is_capturing_camera=*/false, /*is_capturing_microphone=*/true, - /*is_capturing_screen=*/false, /*title=*/u"Test App", + /*is_capturing_screen=*/false, /*title=*/u"Test App2", /*url=*/absl::nullopt)); vc_manager().RegisterCppClient(client2.get(), client2->id_); @@ -167,6 +173,9 @@ vc_manager().GetMediaApps(base::BindLambdaForTesting( [&](VideoConferenceManagerAsh::MediaApps apps) { EXPECT_EQ(apps.size(), 3UL); + EXPECT_EQ(apps[0]->title, u"Test App1"); + EXPECT_EQ(apps[1]->title, u"Test App2"); + EXPECT_EQ(apps[2]->title, u"Test App0"); auto status = GetAggregatedCaptureStatus(std::move(apps));
diff --git a/chrome/browser/ash/wallpaper_handlers/test_wallpaper_fetcher_delegate.cc b/chrome/browser/ash/wallpaper_handlers/test_wallpaper_fetcher_delegate.cc index 9e5c220..ba983b6 100644 --- a/chrome/browser/ash/wallpaper_handlers/test_wallpaper_fetcher_delegate.cc +++ b/chrome/browser/ash/wallpaper_handlers/test_wallpaper_fetcher_delegate.cc
@@ -30,4 +30,32 @@ collection_id); } +std::unique_ptr<GooglePhotosAlbumsFetcher> +TestWallpaperFetcherDelegate::CreateGooglePhotosAlbumsFetcher( + Profile* profile) const { + return std::make_unique<testing::NiceMock<MockGooglePhotosAlbumsFetcher>>( + profile); +} + +std::unique_ptr<GooglePhotosSharedAlbumsFetcher> +TestWallpaperFetcherDelegate::CreateGooglePhotosSharedAlbumsFetcher( + Profile* profile) const { + return std::make_unique< + testing::NiceMock<MockGooglePhotosSharedAlbumsFetcher>>(profile); +} + +std::unique_ptr<GooglePhotosEnabledFetcher> +TestWallpaperFetcherDelegate::CreateGooglePhotosEnabledFetcher( + Profile* profile) const { + return std::make_unique<testing::NiceMock<MockGooglePhotosEnabledFetcher>>( + profile); +} + +std::unique_ptr<GooglePhotosPhotosFetcher> +TestWallpaperFetcherDelegate::CreateGooglePhotosPhotosFetcher( + Profile* profile) const { + return std::make_unique<testing::NiceMock<MockGooglePhotosPhotosFetcher>>( + profile); +} + } // namespace wallpaper_handlers
diff --git a/chrome/browser/ash/wallpaper_handlers/test_wallpaper_fetcher_delegate.h b/chrome/browser/ash/wallpaper_handlers/test_wallpaper_fetcher_delegate.h index b544537..fad1e4c 100644 --- a/chrome/browser/ash/wallpaper_handlers/test_wallpaper_fetcher_delegate.h +++ b/chrome/browser/ash/wallpaper_handlers/test_wallpaper_fetcher_delegate.h
@@ -30,6 +30,14 @@ CreateBackdropCollectionInfoFetcher() const override; std::unique_ptr<BackdropImageInfoFetcher> CreateBackdropImageInfoFetcher( const std::string& collection_id) const override; + std::unique_ptr<GooglePhotosAlbumsFetcher> CreateGooglePhotosAlbumsFetcher( + Profile* profile) const override; + std::unique_ptr<GooglePhotosSharedAlbumsFetcher> + CreateGooglePhotosSharedAlbumsFetcher(Profile* profile) const override; + std::unique_ptr<GooglePhotosEnabledFetcher> CreateGooglePhotosEnabledFetcher( + Profile* profile) const override; + std::unique_ptr<GooglePhotosPhotosFetcher> CreateGooglePhotosPhotosFetcher( + Profile* profile) const override; }; } // namespace wallpaper_handlers
diff --git a/chrome/browser/ash/wallpaper_handlers/wallpaper_fetcher_delegate.cc b/chrome/browser/ash/wallpaper_handlers/wallpaper_fetcher_delegate.cc index 3f20884..983e1ca 100644 --- a/chrome/browser/ash/wallpaper_handlers/wallpaper_fetcher_delegate.cc +++ b/chrome/browser/ash/wallpaper_handlers/wallpaper_fetcher_delegate.cc
@@ -8,6 +8,7 @@ #include <string> #include "chrome/browser/ash/wallpaper_handlers/wallpaper_handlers.h" +#include "chrome/browser/profiles/profile.h" #include "third_party/abseil-cpp/absl/memory/memory.h" namespace wallpaper_handlers { @@ -29,4 +30,32 @@ return absl::WrapUnique(new BackdropImageInfoFetcher(collection_id)); } +std::unique_ptr<GooglePhotosAlbumsFetcher> +WallpaperFetcherDelegateImpl::CreateGooglePhotosAlbumsFetcher( + Profile* profile) const { + // Use `WrapUnique` to access the protected constructor. + return absl::WrapUnique(new GooglePhotosAlbumsFetcher(profile)); +} + +std::unique_ptr<GooglePhotosSharedAlbumsFetcher> +WallpaperFetcherDelegateImpl::CreateGooglePhotosSharedAlbumsFetcher( + Profile* profile) const { + // Use `WrapUnique` to access the protected constructor. + return absl::WrapUnique(new GooglePhotosSharedAlbumsFetcher(profile)); +} + +std::unique_ptr<GooglePhotosEnabledFetcher> +WallpaperFetcherDelegateImpl::CreateGooglePhotosEnabledFetcher( + Profile* profile) const { + // Use `WrapUnique` to access the protected constructor. + return absl::WrapUnique(new GooglePhotosEnabledFetcher(profile)); +} + +std::unique_ptr<GooglePhotosPhotosFetcher> +WallpaperFetcherDelegateImpl::CreateGooglePhotosPhotosFetcher( + Profile* profile) const { + // Use `WrapUnique` to access the protected constructor. + return absl::WrapUnique(new GooglePhotosPhotosFetcher(profile)); +} + } // namespace wallpaper_handlers
diff --git a/chrome/browser/ash/wallpaper_handlers/wallpaper_fetcher_delegate.h b/chrome/browser/ash/wallpaper_handlers/wallpaper_fetcher_delegate.h index 0be12850..71ae383 100644 --- a/chrome/browser/ash/wallpaper_handlers/wallpaper_fetcher_delegate.h +++ b/chrome/browser/ash/wallpaper_handlers/wallpaper_fetcher_delegate.h
@@ -8,10 +8,16 @@ #include <memory> #include <string> +#include "chrome/browser/profiles/profile.h" + namespace wallpaper_handlers { class BackdropCollectionInfoFetcher; class BackdropImageInfoFetcher; +class GooglePhotosAlbumsFetcher; +class GooglePhotosSharedAlbumsFetcher; +class GooglePhotosEnabledFetcher; +class GooglePhotosPhotosFetcher; // Delegate class for creating backdrop fetchers. Abstract class to allow // mocking out in test. @@ -24,6 +30,18 @@ virtual std::unique_ptr<BackdropImageInfoFetcher> CreateBackdropImageInfoFetcher(const std::string& collection_id) const = 0; + + virtual std::unique_ptr<GooglePhotosAlbumsFetcher> + CreateGooglePhotosAlbumsFetcher(Profile* profile) const = 0; + + virtual std::unique_ptr<GooglePhotosSharedAlbumsFetcher> + CreateGooglePhotosSharedAlbumsFetcher(Profile* profile) const = 0; + + virtual std::unique_ptr<GooglePhotosEnabledFetcher> + CreateGooglePhotosEnabledFetcher(Profile* profile) const = 0; + + virtual std::unique_ptr<GooglePhotosPhotosFetcher> + CreateGooglePhotosPhotosFetcher(Profile* profile) const = 0; }; class WallpaperFetcherDelegateImpl : public WallpaperFetcherDelegate { @@ -42,6 +60,18 @@ std::unique_ptr<BackdropImageInfoFetcher> CreateBackdropImageInfoFetcher( const std::string& collection_id) const override; + + std::unique_ptr<GooglePhotosAlbumsFetcher> CreateGooglePhotosAlbumsFetcher( + Profile* profile) const override; + + std::unique_ptr<GooglePhotosSharedAlbumsFetcher> + CreateGooglePhotosSharedAlbumsFetcher(Profile* profile) const override; + + std::unique_ptr<GooglePhotosEnabledFetcher> CreateGooglePhotosEnabledFetcher( + Profile* profile) const override; + + std::unique_ptr<GooglePhotosPhotosFetcher> CreateGooglePhotosPhotosFetcher( + Profile* profile) const override; }; } // namespace wallpaper_handlers
diff --git a/chrome/browser/ash/wallpaper_handlers/wallpaper_handlers.h b/chrome/browser/ash/wallpaper_handlers/wallpaper_handlers.h index 0a6da7a..f53b9fc 100644 --- a/chrome/browser/ash/wallpaper_handlers/wallpaper_handlers.h +++ b/chrome/browser/ash/wallpaper_handlers/wallpaper_handlers.h
@@ -13,6 +13,7 @@ #include "base/functional/callback_forward.h" #include "base/scoped_observation.h" #include "base/values.h" +#include "chrome/browser/ash/wallpaper_handlers/wallpaper_fetcher_delegate.h" #include "components/signin/public/identity_manager/identity_manager.h" #include "url/gurl.h" @@ -237,8 +238,6 @@ class GooglePhotosAlbumsFetcher : public GooglePhotosFetcher<GooglePhotosAlbumsCbkArgs> { public: - explicit GooglePhotosAlbumsFetcher(Profile* profile); - GooglePhotosAlbumsFetcher(const GooglePhotosAlbumsFetcher&) = delete; GooglePhotosAlbumsFetcher& operator=(const GooglePhotosAlbumsFetcher&) = delete; @@ -250,6 +249,10 @@ base::OnceCallback<void(GooglePhotosAlbumsCbkArgs)> callback); protected: + // Protected constructor forces creation via `WallpaperFetcherDelegate` to + // allow mocking in test code. + explicit GooglePhotosAlbumsFetcher(Profile* profile); + // GooglePhotosFetcher: GooglePhotosAlbumsCbkArgs ParseResponse( const base::Value::Dict* response) override; @@ -257,6 +260,10 @@ const GooglePhotosAlbumsCbkArgs& result) override; private: + // Allow delegate to see the constructor. + friend class WallpaperFetcherDelegateImpl; + friend class GooglePhotosAlbumsFetcherTest; + int albums_api_refresh_counter_ = 0; }; @@ -266,8 +273,6 @@ class GooglePhotosSharedAlbumsFetcher : public GooglePhotosFetcher<GooglePhotosAlbumsCbkArgs> { public: - explicit GooglePhotosSharedAlbumsFetcher(Profile* profile); - GooglePhotosSharedAlbumsFetcher(const GooglePhotosSharedAlbumsFetcher&) = delete; GooglePhotosSharedAlbumsFetcher& operator=( @@ -280,6 +285,10 @@ base::OnceCallback<void(GooglePhotosAlbumsCbkArgs)> callback); protected: + // Protected constructor forces creation via `WallpaperFetcherDelegate` to + // allow mocking in test code. + explicit GooglePhotosSharedAlbumsFetcher(Profile* profile); + // GooglePhotosFetcher: GooglePhotosAlbumsCbkArgs ParseResponse( const base::Value::Dict* response) override; @@ -287,6 +296,8 @@ const GooglePhotosAlbumsCbkArgs& result) override; private: + friend class WallpaperFetcherDelegateImpl; + int shared_albums_api_refresh_counter_ = 0; }; @@ -295,8 +306,6 @@ class GooglePhotosEnabledFetcher : public GooglePhotosFetcher<GooglePhotosEnablementState> { public: - explicit GooglePhotosEnabledFetcher(Profile* profile); - GooglePhotosEnabledFetcher(const GooglePhotosEnabledFetcher&) = delete; GooglePhotosEnabledFetcher& operator=(const GooglePhotosEnabledFetcher&) = delete; @@ -307,11 +316,19 @@ base::OnceCallback<void(GooglePhotosEnablementState)> callback); protected: + // Protected constructor forces creation via `WallpaperFetcherDelegate` to + // allow mocking in test code. + explicit GooglePhotosEnabledFetcher(Profile* profile); + // GooglePhotosFetcher: GooglePhotosEnablementState ParseResponse( const base::Value::Dict* response) override; absl::optional<size_t> GetResultCount( const GooglePhotosEnablementState& result) override; + + private: + friend class WallpaperFetcherDelegateImpl; + friend class GooglePhotosEnabledFetcherTest; }; using GooglePhotosPhotosCbkArgs = @@ -320,8 +337,6 @@ class GooglePhotosPhotosFetcher : public GooglePhotosFetcher<GooglePhotosPhotosCbkArgs> { public: - explicit GooglePhotosPhotosFetcher(Profile* profile); - GooglePhotosPhotosFetcher(const GooglePhotosPhotosFetcher&) = delete; GooglePhotosPhotosFetcher& operator=(const GooglePhotosPhotosFetcher&) = delete; @@ -336,6 +351,10 @@ base::OnceCallback<void(GooglePhotosPhotosCbkArgs)> callback); protected: + // Protected constructor forces creation via `WallpaperFetcherDelegate` to + // allow mocking in test code. + explicit GooglePhotosPhotosFetcher(Profile* profile); + // GooglePhotosFetcher: absl::optional<base::Value> CreateErrorResponse(int error_code) override; GooglePhotosPhotosCbkArgs ParseResponse( @@ -344,6 +363,9 @@ const GooglePhotosPhotosCbkArgs& result) override; private: + friend class WallpaperFetcherDelegateImpl; + friend class GooglePhotosPhotosFetcherTest; + int photos_api_refresh_counter_ = 0; };
diff --git a/chrome/browser/ash/wallpaper_handlers/wallpaper_handlers_unittest.cc b/chrome/browser/ash/wallpaper_handlers/wallpaper_handlers_unittest.cc new file mode 100644 index 0000000..bf8c85d --- /dev/null +++ b/chrome/browser/ash/wallpaper_handlers/wallpaper_handlers_unittest.cc
@@ -0,0 +1,453 @@ +// 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/ash/wallpaper_handlers/wallpaper_handlers.h" + +#include <memory> +#include <string> +#include <vector> + +#include "ash/webui/personalization_app/mojom/personalization_app.mojom.h" +#include "base/json/json_reader.h" +#include "base/memory/raw_ptr.h" +#include "base/strings/string_piece.h" +#include "base/values.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/browser/ash/wallpaper_handlers/wallpaper_fetcher_delegate.h" +#include "chrome/test/base/scoped_testing_local_state.h" +#include "chrome/test/base/testing_browser_process.h" +#include "chrome/test/base/testing_profile.h" +#include "chrome/test/base/testing_profile_manager.h" +#include "components/user_manager/scoped_user_manager.h" +#include "content/public/browser/browser_context.h" +#include "content/public/test/browser_task_environment.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/icu/source/i18n/unicode/timezone.h" + +namespace wallpaper_handlers { + +namespace { + +constexpr char kFakeTestEmail[] = "fakeemail@personalization"; + +constexpr char kGooglePhotosResumeTokenOnlyResponse[] = + "{\"resumeToken\": \"token\"}"; +constexpr char kGooglePhotosResumeToken[] = "token"; + +constexpr char kGooglePhotosPhotosFullResponse[] = + "{" + " \"item\": [ {" + " \"itemId\": {" + " \"mediaKey\": \"photoId\"" + " }," + " \"dedupKey\": \"dedupKey\"," + " \"filename\": \"photoName.png\"," + " \"creationTimestamp\": \"2021-12-31T07:07:07.000Z\"," + " \"photo\": {" + " \"servingUrl\": \"https://www.google.com/\"" + " }," + " \"locationFeature\": {" + " \"name\": [ {" + " \"text\": \"home\"" + " } ]" + " }" + " } ]," + " \"resumeToken\": \"token\"" + "}"; + +constexpr char kGooglePhotosPhotosSingleItemResponse[] = + "{" + " \"item\": {" + " \"itemId\": {" + " \"mediaKey\": \"photoId\"" + " }," + " \"dedupKey\": \"dedupKey\"," + " \"filename\": \"photoName.png\"," + " \"creationTimestamp\": \"2021-12-31T07:07:07.000Z\"," + " \"photo\": {" + " \"servingUrl\": \"https://www.google.com/\"" + " }," + " \"locationFeature\": {" + " \"name\": [ {" + " \"text\": \"home\"" + " } ]" + " }" + " }" + "}"; + +constexpr char kGooglePhotosAlbumsFullResponse[] = + "{" + " \"collection\": [ {" + " \"collectionId\": {" + " \"mediaKey\": \"albumId\"" + " }," + " \"coverItemServingUrl\": \"https://www.google.com/\"," + " \"name\": \"title\"," + " \"numPhotos\": \"1\"," + " \"latestModificationTimestamp\": \"2021-12-31T07:07:07.000Z\"" + " } ]," + " \"resumeToken\": \"token\"" + "}"; + +// Parses `json` as a value dictionary. A test calling this function will fail +// if `json` is not appropriately formatted. +base::Value::Dict JsonToDict(base::StringPiece json) { + absl::optional<base::Value> parsed_json = base::JSONReader::Read(json); + EXPECT_TRUE(parsed_json.has_value() && parsed_json->is_dict()); + return std::move(*parsed_json).TakeDict(); +} + +// Returns a non-null pointer to the photo in a hypothetical Google Photos +// photos query response. A test calling this function will fail if the response +// does not contain exactly one photo. +base::Value::Dict* GetPhotoFromGooglePhotosPhotosResponse( + base::Value::Dict* response) { + EXPECT_TRUE(response); + auto* photos = response->FindList("item"); + EXPECT_TRUE(photos && photos->size() == 1); + auto* photo = photos->front().GetIfDict(); + EXPECT_TRUE(photo); + return photo; +} + +// Returns a non-null pointer to the album in a hypothetical Google Photos +// albums query response. A test calling this function will fail if the response +// does not contain exactly one album. +base::Value::Dict* GetAlbumFromGooglePhotosAlbumsResponse( + base::Value::Dict* response) { + EXPECT_TRUE(response); + auto* albums = response->FindList("collection"); + EXPECT_TRUE(albums && albums->size() == 1); + auto* album = albums->front().GetIfDict(); + EXPECT_TRUE(album); + return album; +} + +using ash::personalization_app::mojom::FetchGooglePhotosAlbumsResponse; +using ash::personalization_app::mojom::FetchGooglePhotosPhotosResponse; +using ash::personalization_app::mojom::GooglePhotosAlbum; +using ash::personalization_app::mojom::GooglePhotosAlbumPtr; +using ash::personalization_app::mojom::GooglePhotosEnablementState; +using ash::personalization_app::mojom::GooglePhotosPhoto; +using ash::personalization_app::mojom::GooglePhotosPhotoPtr; + +} // namespace + +class GooglePhotosFetcherTestBase : public testing::Test { + public: + GooglePhotosFetcherTestBase() + : scoped_user_manager_(std::make_unique<ash::FakeChromeUserManager>()), + profile_manager_(TestingBrowserProcess::GetGlobal()) {} + + GooglePhotosFetcherTestBase(const GooglePhotosFetcherTestBase&) = delete; + GooglePhotosFetcherTestBase& operator=(const GooglePhotosFetcherTestBase&) = + delete; + ~GooglePhotosFetcherTestBase() override = default; + + TestingProfile* profile() { return profile_; } + + protected: + // testing::Test: + void SetUp() override { + ASSERT_TRUE(profile_manager_.SetUp()); + profile_ = profile_manager_.CreateTestingProfile(kFakeTestEmail, + /*is_main_profile=*/true); + } + + private: + content::BrowserTaskEnvironment task_environment_; + user_manager::ScopedUserManager scoped_user_manager_; + TestingProfileManager profile_manager_; + raw_ptr<TestingProfile> profile_; +}; + +class GooglePhotosEnabledFetcherTest : public GooglePhotosFetcherTestBase { + public: + GooglePhotosEnablementState ParseResponse(base::Value::Dict* response) { + return google_photos_enabled_fetcher_->ParseResponse(response); + } + + absl::optional<size_t> GetResultCount( + const GooglePhotosEnablementState& result) { + return google_photos_enabled_fetcher_->GetResultCount(result); + } + + protected: + void SetUp() override { + GooglePhotosFetcherTestBase::SetUp(); + google_photos_enabled_fetcher_ = + std::make_unique<WallpaperFetcherDelegateImpl>() + ->CreateGooglePhotosEnabledFetcher(profile()); + } + + private: + std::unique_ptr<GooglePhotosEnabledFetcher> google_photos_enabled_fetcher_; +}; + +TEST_F(GooglePhotosEnabledFetcherTest, ParseGooglePhotosEnabled) { + // Parse an absent response (simulating a fetching error). + auto result = GooglePhotosEnablementState::kError; + EXPECT_EQ(ParseResponse(nullptr), result); + EXPECT_EQ(GetResultCount(result), absl::nullopt); + + // Parse a response without an enabled state. + base::Value::Dict response; + EXPECT_EQ(ParseResponse(&response), result); + EXPECT_EQ(GetResultCount(result), absl::nullopt); + + // Parse a response with an unknown enabled state. + response.SetByDottedPath("status.userState", "UNKNOWN_STATUS_STATE"); + EXPECT_EQ(ParseResponse(&response), result); + EXPECT_EQ(GetResultCount(result), absl::nullopt); + + // Parse a response indicating that the user cannot access Google Photos data. + response.SetByDottedPath("status.userState", "USER_DASHER_DISABLED"); + result = GooglePhotosEnablementState::kDisabled; + EXPECT_EQ(ParseResponse(&response), result); + EXPECT_EQ(GetResultCount(result), absl::make_optional<size_t>(1u)); + + // Parse a response indicating that the user can access Google Photos data. + response.SetByDottedPath("status.userState", "USER_PERMITTED"); + result = GooglePhotosEnablementState::kEnabled; + EXPECT_EQ(ParseResponse(&response), result); + EXPECT_EQ(GetResultCount(result), absl::make_optional<size_t>(1u)); +} + +class GooglePhotosPhotosFetcherTest : public GooglePhotosFetcherTestBase { + public: + GooglePhotosPhotosCbkArgs ParseResponse(const base::Value::Dict* response) { + return google_photos_photos_fetcher_->ParseResponse(response); + } + + absl::optional<size_t> GetResultCount( + const GooglePhotosPhotosCbkArgs& result) { + return google_photos_photos_fetcher_->GetResultCount(result); + } + + protected: + void SetUp() override { + GooglePhotosFetcherTestBase::SetUp(); + google_photos_photos_fetcher_ = + std::make_unique<WallpaperFetcherDelegateImpl>() + ->CreateGooglePhotosPhotosFetcher(profile()); + } + + private: + std::unique_ptr<GooglePhotosPhotosFetcher> google_photos_photos_fetcher_; +}; + +TEST_F(GooglePhotosPhotosFetcherTest, ParseGooglePhotosPhotosAbsentPhoto) { + // Parse an absent response (simulating a fetching error). + auto result = FetchGooglePhotosPhotosResponse::New(); + EXPECT_EQ(ParseResponse(nullptr), result); + EXPECT_EQ(GetResultCount(result), absl::nullopt); + + // Parse a response with no resume token or photos. + base::Value::Dict empty_response; + EXPECT_EQ(ParseResponse(&empty_response), result); + EXPECT_EQ(GetResultCount(result), absl::nullopt); + + // Parse a response with a resume token and no photos. + auto response = JsonToDict(kGooglePhotosResumeTokenOnlyResponse); + result = FetchGooglePhotosPhotosResponse::New(absl::nullopt, + kGooglePhotosResumeToken); + EXPECT_EQ(ParseResponse(&response), result); + EXPECT_EQ(GetResultCount(result), absl::optional<size_t>()); +} + +TEST_F(GooglePhotosPhotosFetcherTest, ParsePhotosInvalidPhoto) { + auto result = FetchGooglePhotosPhotosResponse::New( + std::vector<GooglePhotosPhotoPtr>(), kGooglePhotosResumeToken); + + // Parse one-photo responses where one of the photo's fields is missing. + for (const auto* const path : {"itemId.mediaKey", "filename", + "creationTimestamp", "photo.servingUrl"}) { + auto response = JsonToDict(kGooglePhotosPhotosFullResponse); + auto* photo = GetPhotoFromGooglePhotosPhotosResponse(&response); + photo->RemoveByDottedPath(path); + EXPECT_EQ(ParseResponse(&response), result); + EXPECT_EQ(GetResultCount(result), absl::make_optional<size_t>(0u)); + } + + // Parse one-photo responses where one of the photo's fields has an invalid + // value. + std::vector<std::pair<std::string, std::string>> invalid_field_test_cases = { + {"creationTimestamp", ""}, + {"creationTimestamp", "Bad timestamp"}, + {"creationTimestamp", "2021T07:07:07.000Z"}, + {"creationTimestamp", "31T07:07:07.000Z"}, + {"creationTimestamp", "12T07:07:07.000Z"}, + {"creationTimestamp", "12-31T07:07:07.000Z"}, + {"creationTimestamp", "2021-31T07:07:07.000Z"}, + {"creationTimestamp", "2021-12T07:07:07.000Z"}, + {"creationTimestamp", "-2021-12-31T07:07:07.000Z"}}; + for (const auto& kv : invalid_field_test_cases) { + auto response = JsonToDict(kGooglePhotosPhotosFullResponse); + auto* photo = GetPhotoFromGooglePhotosPhotosResponse(&response); + photo->SetByDottedPath(kv.first, kv.second); + EXPECT_EQ(ParseResponse(&response), result); + EXPECT_EQ(GetResultCount(result), absl::make_optional<size_t>(0u)); + } +} + +TEST_F(GooglePhotosPhotosFetcherTest, ParsePhotosValidPhoto) { + // Ensure that photo timestamps resolve to the same date on all testing + // platforms. + icu::TimeZone::adoptDefault(icu::TimeZone::createTimeZone("UTC")); + + // Parse a response with a valid photo and a resume token. + auto valid_photos_vector = std::vector<GooglePhotosPhotoPtr>(); + valid_photos_vector.push_back(GooglePhotosPhoto::New( + "photoId", "dedupKey", "photoName", u"Friday, December 31, 2021", + GURL("https://www.google.com/"), "home")); + auto response = JsonToDict(kGooglePhotosPhotosFullResponse); + auto result = FetchGooglePhotosPhotosResponse::New( + mojo::Clone(valid_photos_vector), kGooglePhotosResumeToken); + EXPECT_EQ(ParseResponse(&response), result); + EXPECT_EQ(GetResultCount(result), + absl::make_optional<size_t>(valid_photos_vector.size())); + + // Parse a response with a valid photo and no resume token. + response.Remove("resumeToken"); + result = FetchGooglePhotosPhotosResponse::New( + mojo::Clone(valid_photos_vector), absl::nullopt); + EXPECT_EQ(ParseResponse(&response), result); + EXPECT_EQ(GetResultCount(result), + absl::make_optional<size_t>(valid_photos_vector.size())); + + // Parse a response with a single valid photo not in a list. + response = JsonToDict(kGooglePhotosPhotosSingleItemResponse); + EXPECT_EQ(ParseResponse(&response), result); + EXPECT_EQ(GetResultCount(result), + absl::make_optional<size_t>(valid_photos_vector.size())); + + // Parse a response with a valid photo and no dedup key. + auto valid_photos_vector_without_dedup_key = + std::vector<GooglePhotosPhotoPtr>(); + valid_photos_vector_without_dedup_key.push_back(GooglePhotosPhoto::New( + "photoId", absl::nullopt, "photoName", u"Friday, December 31, 2021", + GURL("https://www.google.com/"), "home")); + response.RemoveByDottedPath("item.dedupKey"); + result = FetchGooglePhotosPhotosResponse::New( + mojo::Clone(valid_photos_vector_without_dedup_key), absl::nullopt); + EXPECT_EQ(result, ParseResponse(&response)); + EXPECT_EQ(GetResultCount(result), + absl::make_optional<size_t>( + valid_photos_vector_without_dedup_key.size())); + + // Parse a response with a valid photo and no location. + auto valid_photos_vector_without_location = + std::vector<GooglePhotosPhotoPtr>(); + valid_photos_vector_without_location.push_back(GooglePhotosPhoto::New( + "photoId", absl::nullopt, "photoName", u"Friday, December 31, 2021", + GURL("https://www.google.com/"), absl::nullopt)); + auto* name_list = response.FindListByDottedPath("item.locationFeature.name"); + EXPECT_FALSE(name_list->empty()); + name_list->clear(); + result = FetchGooglePhotosPhotosResponse::New( + mojo::Clone(valid_photos_vector_without_location), absl::nullopt); + EXPECT_EQ(result, ParseResponse(&response)); + EXPECT_EQ( + GetResultCount(result), + absl::make_optional<size_t>(valid_photos_vector_without_location.size())); +} + +class GooglePhotosAlbumsFetcherTest : public GooglePhotosFetcherTestBase { + public: + GooglePhotosAlbumsCbkArgs ParseResponse(const base::Value::Dict* response) { + return google_photos_albums_fetcher_->ParseResponse(response); + } + + absl::optional<size_t> GetResultCount( + const GooglePhotosAlbumsCbkArgs& result) { + return google_photos_albums_fetcher_->GetResultCount(result); + } + + protected: + void SetUp() override { + GooglePhotosFetcherTestBase::SetUp(); + google_photos_albums_fetcher_ = + std::make_unique<WallpaperFetcherDelegateImpl>() + ->CreateGooglePhotosAlbumsFetcher(profile()); + } + + private: + std::unique_ptr<GooglePhotosAlbumsFetcher> google_photos_albums_fetcher_; +}; + +TEST_F(GooglePhotosAlbumsFetcherTest, ParseAlbumsAbsentAlbum) { + // Parse an absent response (simulating a fetching error). + auto result = FetchGooglePhotosAlbumsResponse::New(); + EXPECT_EQ(ParseResponse(nullptr), result); + EXPECT_EQ(GetResultCount(result), absl::nullopt); + + // Parse a response with no resume token or albums. + base::Value::Dict empty_response; + EXPECT_EQ(ParseResponse(&empty_response), result); + + // Parse a response with a resume token and no albums. + auto response = JsonToDict(kGooglePhotosResumeTokenOnlyResponse); + result = FetchGooglePhotosAlbumsResponse::New(absl::nullopt, + kGooglePhotosResumeToken); + EXPECT_EQ(ParseResponse(&response), result); + EXPECT_EQ(GetResultCount(result), absl::nullopt); +} + +TEST_F(GooglePhotosAlbumsFetcherTest, ParseAlbumsInvalidAlbum) { + auto result = FetchGooglePhotosAlbumsResponse::New( + std::vector<GooglePhotosAlbumPtr>(), kGooglePhotosResumeToken); + + // Parse one-album responses where one of the album's fields is missing. + for (const auto* const path : + {"collectionId.mediaKey", "name", "numPhotos", "coverItemServingUrl"}) { + auto response = JsonToDict(kGooglePhotosAlbumsFullResponse); + auto* album = GetAlbumFromGooglePhotosAlbumsResponse(&response); + album->RemoveByDottedPath(path); + EXPECT_EQ(ParseResponse(&response), result); + EXPECT_EQ(GetResultCount(result), absl::make_optional<size_t>(0u)); + } + + // Parse one-album responses where one of the album's fields has an invalid + // value. + std::vector<std::pair<std::string, std::string>> invalid_field_test_cases = { + {"numPhotos", ""}, + {"numPhotos", "NaN"}, + {"numPhotos", "-1"}, + {"numPhotos", "0"}}; + for (const auto& kv : invalid_field_test_cases) { + auto response = JsonToDict(kGooglePhotosAlbumsFullResponse); + auto* album = GetAlbumFromGooglePhotosAlbumsResponse(&response); + album->SetByDottedPath(kv.first, kv.second); + EXPECT_EQ(ParseResponse(&response), result); + EXPECT_EQ(GetResultCount(result), absl::make_optional<size_t>(0u)); + } +} + +TEST_F(GooglePhotosAlbumsFetcherTest, ParseAlbumsValidAlbum) { + // Parse a response with a valid album and a resume token. + auto response = JsonToDict(kGooglePhotosAlbumsFullResponse); + auto valid_albums_vector = std::vector<GooglePhotosAlbumPtr>(); + base::Time timestamp; + EXPECT_TRUE( + base::Time::FromUTCString("2021-12-31T07:07:07.000Z", ×tamp)); + valid_albums_vector.push_back(GooglePhotosAlbum::New( + "albumId", "title", 1, GURL("https://www.google.com/"), timestamp, + /*is_shared=*/false)); + auto result = FetchGooglePhotosAlbumsResponse::New( + mojo::Clone(valid_albums_vector), kGooglePhotosResumeToken); + EXPECT_EQ(ParseResponse(&response), result); + EXPECT_EQ(GetResultCount(result), + absl::make_optional<size_t>(valid_albums_vector.size())); + + // Parse a response with a valid album and no resume token. + response.Remove("resumeToken"); + result = FetchGooglePhotosAlbumsResponse::New( + mojo::Clone(valid_albums_vector), absl::nullopt); + EXPECT_EQ(ParseResponse(&response), result); + EXPECT_EQ(GetResultCount(result), + absl::make_optional<size_t>(valid_albums_vector.size())); +} + +} // namespace wallpaper_handlers
diff --git a/chrome/browser/ash/web_applications/personalization_app/personalization_app_wallpaper_provider_impl.cc b/chrome/browser/ash/web_applications/personalization_app/personalization_app_wallpaper_provider_impl.cc index b325f44d..354f107 100644 --- a/chrome/browser/ash/web_applications/personalization_app/personalization_app_wallpaper_provider_impl.cc +++ b/chrome/browser/ash/web_applications/personalization_app/personalization_app_wallpaper_provider_impl.cc
@@ -257,8 +257,7 @@ if (!google_photos_albums_fetcher_) { google_photos_albums_fetcher_ = - std::make_unique<wallpaper_handlers::GooglePhotosAlbumsFetcher>( - profile_); + wallpaper_fetcher_delegate_->CreateGooglePhotosAlbumsFetcher(profile_); } google_photos_albums_fetcher_->AddRequestAndStartIfNecessary( resume_token, std::move(callback)); @@ -276,7 +275,7 @@ if (!google_photos_shared_albums_fetcher_) { google_photos_shared_albums_fetcher_ = - std::make_unique<wallpaper_handlers::GooglePhotosSharedAlbumsFetcher>( + wallpaper_fetcher_delegate_->CreateGooglePhotosSharedAlbumsFetcher( profile_); } google_photos_shared_albums_fetcher_->AddRequestAndStartIfNecessary( @@ -296,8 +295,7 @@ if (!google_photos_enabled_fetcher_) { google_photos_enabled_fetcher_ = - std::make_unique<wallpaper_handlers::GooglePhotosEnabledFetcher>( - profile_); + wallpaper_fetcher_delegate_->CreateGooglePhotosEnabledFetcher(profile_); } // base::Unretained is safe to use because |this| outlives // |google_photos_enabled_fetcher_|. @@ -323,8 +321,7 @@ if (!google_photos_photos_fetcher_) { google_photos_photos_fetcher_ = - std::make_unique<wallpaper_handlers::GooglePhotosPhotosFetcher>( - profile_); + wallpaper_fetcher_delegate_->CreateGooglePhotosPhotosFetcher(profile_); } google_photos_photos_fetcher_->AddRequestAndStartIfNecessary( item_id, album_id, resume_token, /*shuffle=*/false,
diff --git a/chrome/browser/ash/web_applications/personalization_app/personalization_app_wallpaper_provider_impl_unittest.cc b/chrome/browser/ash/web_applications/personalization_app/personalization_app_wallpaper_provider_impl_unittest.cc index 4fe9b5c..5cdfde2 100644 --- a/chrome/browser/ash/web_applications/personalization_app/personalization_app_wallpaper_provider_impl_unittest.cc +++ b/chrome/browser/ash/web_applications/personalization_app/personalization_app_wallpaper_provider_impl_unittest.cc
@@ -9,7 +9,6 @@ #include <utility> #include <vector> -#include "ash/constants/ash_features.h" #include "ash/public/cpp/wallpaper/online_wallpaper_params.h" #include "ash/public/cpp/wallpaper/online_wallpaper_variant.h" #include "ash/public/cpp/wallpaper/wallpaper_controller_client.h" @@ -18,15 +17,12 @@ #include "ash/webui/personalization_app/mojom/personalization_app.mojom-forward.h" #include "ash/webui/personalization_app/mojom/personalization_app.mojom.h" #include "base/functional/callback_helpers.h" -#include "base/json/json_reader.h" #include "base/memory/ref_counted_memory.h" #include "base/memory/scoped_refptr.h" #include "base/notreached.h" #include "base/run_loop.h" #include "base/test/bind.h" -#include "base/values.h" #include "chrome/browser/ash/login/users/fake_chrome_user_manager.h" -#include "chrome/browser/ash/login/users/scoped_test_user_manager.h" #include "chrome/browser/ash/policy/external_data/handlers/device_wallpaper_image_external_data_handler.h" #include "chrome/browser/ash/settings/device_settings_cache.h" #include "chrome/browser/ash/settings/device_settings_service.h" @@ -42,9 +38,7 @@ #include "chrome/test/base/testing_browser_process.h" #include "chrome/test/base/testing_profile.h" #include "chrome/test/base/testing_profile_manager.h" -#include "chrome_device_policy.pb.h" #include "components/keyed_service/core/keyed_service.h" -#include "components/prefs/pref_registry_simple.h" #include "components/prefs/testing_pref_service.h" #include "components/user_manager/known_user.h" #include "components/user_manager/scoped_user_manager.h" @@ -56,7 +50,6 @@ #include "mojo/public/cpp/bindings/remote.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/icu/source/i18n/unicode/timezone.h" #include "third_party/skia/include/core/SkBitmap.h" #include "third_party/skia/include/core/SkColor.h" #include "ui/base/webui/web_ui_util.h" @@ -70,60 +63,6 @@ constexpr char kFakeTestEmail[] = "fakeemail@personalization"; constexpr char kTestGaiaId[] = "1234567890"; -constexpr char kGooglePhotosAlbumsFullResponse[] = - "{" - " \"collection\": [ {" - " \"collectionId\": {" - " \"mediaKey\": \"albumId\"" - " }," - " \"coverItemServingUrl\": \"https://www.google.com/\"," - " \"name\": \"title\"," - " \"numPhotos\": \"1\"," - " \"latestModificationTimestamp\": \"2021-12-31T07:07:07.000Z\"" - " } ]," - " \"resumeToken\": \"token\"" - "}"; -constexpr char kGooglePhotosPhotosFullResponse[] = - "{" - " \"item\": [ {" - " \"itemId\": {" - " \"mediaKey\": \"photoId\"" - " }," - " \"dedupKey\": \"dedupKey\"," - " \"filename\": \"photoName.png\"," - " \"creationTimestamp\": \"2021-12-31T07:07:07.000Z\"," - " \"photo\": {" - " \"servingUrl\": \"https://www.google.com/\"" - " }," - " \"locationFeature\": {" - " \"name\": [ {" - " \"text\": \"home\"" - " } ]" - " }" - " } ]," - " \"resumeToken\": \"token\"" - "}"; -constexpr char kGooglePhotosPhotosSingleItemResponse[] = - "{" - " \"item\": {" - " \"itemId\": {" - " \"mediaKey\": \"photoId\"" - " }," - " \"dedupKey\": \"dedupKey\"," - " \"filename\": \"photoName.png\"," - " \"creationTimestamp\": \"2021-12-31T07:07:07.000Z\"," - " \"photo\": {" - " \"servingUrl\": \"https://www.google.com/\"" - " }," - " \"locationFeature\": {" - " \"name\": [ {" - " \"text\": \"home\"" - " } ]" - " }" - " }" - "}"; -constexpr char kGooglePhotosResumeTokenOnlyResponse[] = - "{\"resumeToken\": \"token\"}"; TestingPrefServiceSimple* RegisterPrefs(TestingPrefServiceSimple* local_state) { ash::device_settings_cache::RegisterPrefs(local_state->registry()); @@ -153,40 +92,6 @@ return gfx::ImageSkia::CreateFrom1xBitmap(bitmap); } -// Parses `json` as a value dictionary. A test calling this function will fail -// if `json` is not appropriately formatted. -base::Value::Dict JsonToDict(base::StringPiece json) { - absl::optional<base::Value> parsed_json = base::JSONReader::Read(json); - EXPECT_TRUE(parsed_json.has_value() && parsed_json->is_dict()); - return std::move(*parsed_json).TakeDict(); -} - -// Returns a non-null pointer to the album in a hypothetical Google Photos -// albums query response. A test calling this function will fail if the response -// does not contain exactly one album. -base::Value::Dict* GetAlbumFromGooglePhotosAlbumsResponse( - base::Value::Dict* response) { - EXPECT_TRUE(response); - auto* albums = response->FindList("collection"); - EXPECT_TRUE(albums && albums->size() == 1); - auto* album = albums->front().GetIfDict(); - EXPECT_TRUE(album); - return album; -} - -// Returns a non-null pointer to the photo in a hypothetical Google Photos -// photos query response. A test calling this function will fail if the response -// does not contain exactly one photo. -base::Value::Dict* GetPhotoFromGooglePhotosPhotosResponse( - base::Value::Dict* response) { - EXPECT_TRUE(response); - auto* photos = response->FindList("item"); - EXPECT_TRUE(photos && photos->size() == 1); - auto* photo = photos->front().GetIfDict(); - EXPECT_TRUE(photo); - return photo; -} - std::unique_ptr<KeyedService> MakeMockPersonalizationAppManager( content::BrowserContext* context) { return std::make_unique<::testing::NiceMock<MockPersonalizationAppManager>>(); @@ -719,317 +624,6 @@ } TEST_F(PersonalizationAppWallpaperProviderImplGooglePhotosTest, - ParseAlbumsAbsentAlbum) { - using ash::personalization_app::mojom::FetchGooglePhotosAlbumsResponse; - using ash::personalization_app::mojom::GooglePhotosAlbumPtr; - - // Mock a fetcher to parse constructed responses. - auto* const google_photos_albums_fetcher = static_cast< - ::testing::NiceMock<wallpaper_handlers::MockGooglePhotosAlbumsFetcher>*>( - delegate()->SetGooglePhotosAlbumsFetcherForTest( - std::make_unique<::testing::NiceMock< - wallpaper_handlers::MockGooglePhotosAlbumsFetcher>>(profile()))); - - // Parse an absent response (simulating a fetching error). - auto result = FetchGooglePhotosAlbumsResponse::New(); - EXPECT_EQ(google_photos_albums_fetcher->ParseResponse(nullptr), result); - EXPECT_EQ(google_photos_albums_fetcher->GetResultCount(result), - absl::nullopt); - - // Parse a response with no resume token or albums. - base::Value::Dict empty_response; - EXPECT_EQ(google_photos_albums_fetcher->ParseResponse(&empty_response), - result); - - // Parse a response with a resume token and no albums. - auto response = JsonToDict(kGooglePhotosResumeTokenOnlyResponse); - result = FetchGooglePhotosAlbumsResponse::New(absl::nullopt, kResumeToken); - EXPECT_EQ(google_photos_albums_fetcher->ParseResponse(&response), result); - EXPECT_EQ(google_photos_albums_fetcher->GetResultCount(result), - absl::nullopt); -} - -TEST_F(PersonalizationAppWallpaperProviderImplGooglePhotosTest, - ParseAlbumsInvalidAlbum) { - using ash::personalization_app::mojom::FetchGooglePhotosAlbumsResponse; - using ash::personalization_app::mojom::GooglePhotosAlbumPtr; - - // Mock a fetcher to parse constructed responses. - auto* const google_photos_albums_fetcher = static_cast< - ::testing::NiceMock<wallpaper_handlers::MockGooglePhotosAlbumsFetcher>*>( - delegate()->SetGooglePhotosAlbumsFetcherForTest( - std::make_unique<::testing::NiceMock< - wallpaper_handlers::MockGooglePhotosAlbumsFetcher>>(profile()))); - - auto result = FetchGooglePhotosAlbumsResponse::New( - std::vector<GooglePhotosAlbumPtr>(), kResumeToken); - - // Parse one-album responses where one of the album's fields is missing. - for (const auto* const path : - {"collectionId.mediaKey", "name", "numPhotos", "coverItemServingUrl"}) { - auto response = JsonToDict(kGooglePhotosAlbumsFullResponse); - auto* album = GetAlbumFromGooglePhotosAlbumsResponse(&response); - album->RemoveByDottedPath(path); - EXPECT_EQ(google_photos_albums_fetcher->ParseResponse(&response), result); - EXPECT_EQ(google_photos_albums_fetcher->GetResultCount(result), - absl::make_optional<size_t>(0u)); - } - - // Parse one-album responses where one of the album's fields has an invalid - // value. - std::vector<std::pair<std::string, std::string>> invalid_field_test_cases = { - {"numPhotos", ""}, - {"numPhotos", "NaN"}, - {"numPhotos", "-1"}, - {"numPhotos", "0"}}; - EXPECT_CALL(*google_photos_albums_fetcher, ParseResponse).Times(4); - for (const auto& kv : invalid_field_test_cases) { - auto response = JsonToDict(kGooglePhotosAlbumsFullResponse); - auto* album = GetAlbumFromGooglePhotosAlbumsResponse(&response); - album->SetByDottedPath(kv.first, kv.second); - EXPECT_EQ(google_photos_albums_fetcher->ParseResponse(&response), result); - EXPECT_EQ(google_photos_albums_fetcher->GetResultCount(result), - absl::make_optional<size_t>(0u)); - } -} - -TEST_F(PersonalizationAppWallpaperProviderImplGooglePhotosTest, - ParseAlbumsValidAlbum) { - using ash::personalization_app::mojom::FetchGooglePhotosAlbumsResponse; - using ash::personalization_app::mojom::GooglePhotosAlbum; - using ash::personalization_app::mojom::GooglePhotosAlbumPtr; - - // Mock a fetcher to parse constructed responses. - auto* const google_photos_albums_fetcher = static_cast< - ::testing::NiceMock<wallpaper_handlers::MockGooglePhotosAlbumsFetcher>*>( - delegate()->SetGooglePhotosAlbumsFetcherForTest( - std::make_unique<::testing::NiceMock< - wallpaper_handlers::MockGooglePhotosAlbumsFetcher>>(profile()))); - - // Parse a response with a valid album and a resume token. - auto response = JsonToDict(kGooglePhotosAlbumsFullResponse); - auto valid_albums_vector = std::vector<GooglePhotosAlbumPtr>(); - base::Time timestamp; - EXPECT_TRUE( - base::Time::FromUTCString("2021-12-31T07:07:07.000Z", ×tamp)); - valid_albums_vector.push_back(GooglePhotosAlbum::New( - "albumId", "title", 1, GURL("https://www.google.com/"), timestamp, - /*is_shared=*/false)); - auto result = FetchGooglePhotosAlbumsResponse::New( - mojo::Clone(valid_albums_vector), kResumeToken); - EXPECT_EQ(google_photos_albums_fetcher->ParseResponse(&response), result); - EXPECT_EQ(google_photos_albums_fetcher->GetResultCount(result), - absl::make_optional<size_t>(valid_albums_vector.size())); - - // Parse a response with a valid album and no resume token. - response.Remove("resumeToken"); - result = FetchGooglePhotosAlbumsResponse::New( - mojo::Clone(valid_albums_vector), absl::nullopt); - EXPECT_EQ(google_photos_albums_fetcher->ParseResponse(&response), result); - EXPECT_EQ(google_photos_albums_fetcher->GetResultCount(result), - absl::make_optional<size_t>(valid_albums_vector.size())); -} - -TEST_F(PersonalizationAppWallpaperProviderImplGooglePhotosTest, ParseEnabled) { - using ash::personalization_app::mojom::GooglePhotosEnablementState; - - // Mock a fetcher to parse constructed responses. - auto* const google_photos_enabled_fetcher = static_cast< - ::testing::NiceMock<wallpaper_handlers::MockGooglePhotosEnabledFetcher>*>( - delegate()->SetGooglePhotosEnabledFetcherForTest( - std::make_unique<::testing::NiceMock< - wallpaper_handlers::MockGooglePhotosEnabledFetcher>>(profile()))); - - // Parse an absent response (simulating a fetching error). - auto result = GooglePhotosEnablementState::kError; - EXPECT_EQ(google_photos_enabled_fetcher->ParseResponse(nullptr), result); - EXPECT_EQ(google_photos_enabled_fetcher->GetResultCount(result), - absl::nullopt); - - // Parse a response without an enabled state. - base::Value::Dict response; - EXPECT_EQ(google_photos_enabled_fetcher->ParseResponse(&response), result); - EXPECT_EQ(google_photos_enabled_fetcher->GetResultCount(result), - absl::nullopt); - - // Parse a response with an unknown enabled state. - response.SetByDottedPath("status.userState", "UNKNOWN_STATUS_STATE"); - EXPECT_EQ(google_photos_enabled_fetcher->ParseResponse(&response), result); - EXPECT_EQ(google_photos_enabled_fetcher->GetResultCount(result), - absl::nullopt); - - // Parse a response indicating that the user cannot access Google Photos data. - response.SetByDottedPath("status.userState", "USER_DASHER_DISABLED"); - result = GooglePhotosEnablementState::kDisabled; - EXPECT_EQ(google_photos_enabled_fetcher->ParseResponse(&response), result); - EXPECT_EQ(google_photos_enabled_fetcher->GetResultCount(result), - absl::make_optional<size_t>(1u)); - - // Parse a response indicating that the user can access Google Photos data. - response.SetByDottedPath("status.userState", "USER_PERMITTED"); - result = GooglePhotosEnablementState::kEnabled; - EXPECT_EQ(google_photos_enabled_fetcher->ParseResponse(&response), result); - EXPECT_EQ(google_photos_enabled_fetcher->GetResultCount(result), - absl::make_optional<size_t>(1u)); -} - -TEST_F(PersonalizationAppWallpaperProviderImplGooglePhotosTest, - ParsePhotosAbsentPhoto) { - using ash::personalization_app::mojom::FetchGooglePhotosPhotosResponse; - using ash::personalization_app::mojom::GooglePhotosPhotoPtr; - - // Mock a fetcher to parse constructed responses. - auto* const google_photos_photos_fetcher = static_cast< - ::testing::NiceMock<wallpaper_handlers::MockGooglePhotosPhotosFetcher>*>( - delegate()->SetGooglePhotosPhotosFetcherForTest( - std::make_unique<::testing::NiceMock< - wallpaper_handlers::MockGooglePhotosPhotosFetcher>>(profile()))); - - // Parse an absent response (simulating a fetching error). - auto result = FetchGooglePhotosPhotosResponse::New(); - EXPECT_EQ(google_photos_photos_fetcher->ParseResponse(nullptr), result); - EXPECT_EQ(google_photos_photos_fetcher->GetResultCount(result), - absl::nullopt); - - // Parse a response with no resume token or photos. - base::Value::Dict empty_response; - EXPECT_EQ(google_photos_photos_fetcher->ParseResponse(&empty_response), - result); - EXPECT_EQ(google_photos_photos_fetcher->GetResultCount(result), - absl::nullopt); - - // Parse a response with a resume token and no photos. - auto response = JsonToDict(kGooglePhotosResumeTokenOnlyResponse); - result = FetchGooglePhotosPhotosResponse::New(absl::nullopt, kResumeToken); - EXPECT_EQ(google_photos_photos_fetcher->ParseResponse(&response), result); - EXPECT_EQ(google_photos_photos_fetcher->GetResultCount(result), - absl::optional<size_t>()); -} - -TEST_F(PersonalizationAppWallpaperProviderImplGooglePhotosTest, - ParsePhotosInvalidPhoto) { - using ash::personalization_app::mojom::FetchGooglePhotosPhotosResponse; - using ash::personalization_app::mojom::GooglePhotosPhotoPtr; - - // Mock a fetcher to parse constructed responses. - auto* const google_photos_photos_fetcher = static_cast< - ::testing::NiceMock<wallpaper_handlers::MockGooglePhotosPhotosFetcher>*>( - delegate()->SetGooglePhotosPhotosFetcherForTest( - std::make_unique<::testing::NiceMock< - wallpaper_handlers::MockGooglePhotosPhotosFetcher>>(profile()))); - - auto result = FetchGooglePhotosPhotosResponse::New( - std::vector<GooglePhotosPhotoPtr>(), kResumeToken); - - // Parse one-photo responses where one of the photo's fields is missing. - for (const auto* const path : {"itemId.mediaKey", "filename", - "creationTimestamp", "photo.servingUrl"}) { - auto response = JsonToDict(kGooglePhotosPhotosFullResponse); - auto* photo = GetPhotoFromGooglePhotosPhotosResponse(&response); - photo->RemoveByDottedPath(path); - EXPECT_EQ(google_photos_photos_fetcher->ParseResponse(&response), result); - EXPECT_EQ(google_photos_photos_fetcher->GetResultCount(result), - absl::make_optional<size_t>(0u)); - } - - // Parse one-photo responses where one of the photo's fields has an invalid - // value. - std::vector<std::pair<std::string, std::string>> invalid_field_test_cases = { - {"creationTimestamp", ""}, - {"creationTimestamp", "Bad timestamp"}, - {"creationTimestamp", "2021T07:07:07.000Z"}, - {"creationTimestamp", "31T07:07:07.000Z"}, - {"creationTimestamp", "12T07:07:07.000Z"}, - {"creationTimestamp", "12-31T07:07:07.000Z"}, - {"creationTimestamp", "2021-31T07:07:07.000Z"}, - {"creationTimestamp", "2021-12T07:07:07.000Z"}, - {"creationTimestamp", "-2021-12-31T07:07:07.000Z"}}; - EXPECT_CALL(*google_photos_photos_fetcher, ParseResponse).Times(9); - for (const auto& kv : invalid_field_test_cases) { - auto response = JsonToDict(kGooglePhotosPhotosFullResponse); - auto* photo = GetPhotoFromGooglePhotosPhotosResponse(&response); - photo->SetByDottedPath(kv.first, kv.second); - EXPECT_EQ(google_photos_photos_fetcher->ParseResponse(&response), result); - EXPECT_EQ(google_photos_photos_fetcher->GetResultCount(result), - absl::make_optional<size_t>(0u)); - } -} - -TEST_F(PersonalizationAppWallpaperProviderImplGooglePhotosTest, - ParsePhotosValidPhoto) { - using ash::personalization_app::mojom::FetchGooglePhotosPhotosResponse; - using ash::personalization_app::mojom::GooglePhotosPhoto; - using ash::personalization_app::mojom::GooglePhotosPhotoPtr; - - // Mock a fetcher to parse constructed responses. - auto* const google_photos_photos_fetcher = static_cast< - ::testing::NiceMock<wallpaper_handlers::MockGooglePhotosPhotosFetcher>*>( - delegate()->SetGooglePhotosPhotosFetcherForTest( - std::make_unique<::testing::NiceMock< - wallpaper_handlers::MockGooglePhotosPhotosFetcher>>(profile()))); - - // Ensure that photo timestamps resolve to the same date on all testing - // platforms. - icu::TimeZone::adoptDefault(icu::TimeZone::createTimeZone("UTC")); - - // Parse a response with a valid photo and a resume token. - auto valid_photos_vector = std::vector<GooglePhotosPhotoPtr>(); - valid_photos_vector.push_back(GooglePhotosPhoto::New( - "photoId", "dedupKey", "photoName", u"Friday, December 31, 2021", - GURL("https://www.google.com/"), "home")); - auto response = JsonToDict(kGooglePhotosPhotosFullResponse); - auto result = FetchGooglePhotosPhotosResponse::New( - mojo::Clone(valid_photos_vector), kResumeToken); - EXPECT_EQ(google_photos_photos_fetcher->ParseResponse(&response), result); - EXPECT_EQ(google_photos_photos_fetcher->GetResultCount(result), - absl::make_optional<size_t>(valid_photos_vector.size())); - - // Parse a response with a valid photo and no resume token. - response.Remove("resumeToken"); - result = FetchGooglePhotosPhotosResponse::New( - mojo::Clone(valid_photos_vector), absl::nullopt); - EXPECT_EQ(google_photos_photos_fetcher->ParseResponse(&response), result); - EXPECT_EQ(google_photos_photos_fetcher->GetResultCount(result), - absl::make_optional<size_t>(valid_photos_vector.size())); - - // Parse a response with a single valid photo not in a list. - response = JsonToDict(kGooglePhotosPhotosSingleItemResponse); - EXPECT_EQ(google_photos_photos_fetcher->ParseResponse(&response), result); - EXPECT_EQ(google_photos_photos_fetcher->GetResultCount(result), - absl::make_optional<size_t>(valid_photos_vector.size())); - - // Parse a response with a valid photo and no dedup key. - auto valid_photos_vector_without_dedup_key = - std::vector<GooglePhotosPhotoPtr>(); - valid_photos_vector_without_dedup_key.push_back(GooglePhotosPhoto::New( - "photoId", absl::nullopt, "photoName", u"Friday, December 31, 2021", - GURL("https://www.google.com/"), "home")); - response.RemoveByDottedPath("item.dedupKey"); - result = FetchGooglePhotosPhotosResponse::New( - mojo::Clone(valid_photos_vector_without_dedup_key), absl::nullopt); - EXPECT_EQ(result, google_photos_photos_fetcher->ParseResponse(&response)); - EXPECT_EQ(google_photos_photos_fetcher->GetResultCount(result), - absl::make_optional<size_t>( - valid_photos_vector_without_dedup_key.size())); - - // Parse a response with a valid photo and no location. - auto valid_photos_vector_without_location = - std::vector<GooglePhotosPhotoPtr>(); - valid_photos_vector_without_location.push_back(GooglePhotosPhoto::New( - "photoId", absl::nullopt, "photoName", u"Friday, December 31, 2021", - GURL("https://www.google.com/"), absl::nullopt)); - auto* name_list = response.FindListByDottedPath("item.locationFeature.name"); - EXPECT_FALSE(name_list->empty()); - name_list->clear(); - result = FetchGooglePhotosPhotosResponse::New( - mojo::Clone(valid_photos_vector_without_location), absl::nullopt); - EXPECT_EQ(result, google_photos_photos_fetcher->ParseResponse(&response)); - EXPECT_EQ( - google_photos_photos_fetcher->GetResultCount(result), - absl::make_optional<size_t>(valid_photos_vector_without_location.size())); -} - -TEST_F(PersonalizationAppWallpaperProviderImplGooglePhotosTest, SelectGooglePhotosPhoto) { test_wallpaper_controller()->ClearCounts(); const std::string photo_id = "OmnisVirLupus";
diff --git a/chrome/browser/bluetooth/chrome_bluetooth_delegate_impl_client.cc b/chrome/browser/bluetooth/chrome_bluetooth_delegate_impl_client.cc index 3a9aa433..25f1c8c 100644 --- a/chrome/browser/bluetooth/chrome_bluetooth_delegate_impl_client.cc +++ b/chrome/browser/bluetooth/chrome_bluetooth_delegate_impl_client.cc
@@ -68,9 +68,7 @@ #else return std::make_unique<permissions::BluetoothScanningPromptDesktop>( frame, event_handler, - CreateExtensionAwareChooserTitle(frame, - IDS_BLUETOOTH_SCANNING_PROMPT_ORIGIN, - IDS_BLUETOOTH_SCANNING_PROMPT_ORIGIN), + CreateChooserTitle(frame, IDS_BLUETOOTH_SCANNING_PROMPT), base::BindOnce(chrome::ShowDeviceChooserDialog, frame)); #endif }
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd index f705411..57f8424a 100644 --- a/chrome/browser/browser_resources.grd +++ b/chrome/browser/browser_resources.grd
@@ -214,8 +214,10 @@ <include name="IDR_PARENT_ACCESS_BEFORE_JS" file="${root_gen_dir}\chrome\browser\resources\chromeos\parent_access\parent_access_before.js" use_base_dir="false" type="BINDATA" /> <include name="IDR_PARENT_ACCESS_DISABLED_JS" file="${root_gen_dir}\chrome\browser\resources\chromeos\parent_access\parent_access_disabled.js" use_base_dir="false" type="BINDATA" /> <include name="IDR_PARENT_ACCESS_WEBVIEW_MANAGER_JS" file="resources\chromeos\parent_access\webview_manager.js" type="BINDATA" /> + <include name="IDR_PARENT_ACCESS_UTILS_JS" file="resources\chromeos\parent_access\utils.js" type="BINDATA" /> <include name="IDR_LOCAL_WEB_APPROVALS_AFTER_JS" file="${root_gen_dir}\chrome\browser\resources\chromeos\parent_access\flows\local_web_approvals_after.js" use_base_dir="false" type="BINDATA" /> <include name="IDR_EXTENSION_APPROVALS_DISABLED_JS" file="${root_gen_dir}\chrome\browser\resources\chromeos\parent_access\flows\extension_approvals_disabled.js" use_base_dir="false" type="BINDATA" /> + <include name="IDR_EXTENSION_APPROVALS_BEFORE_JS" file="${root_gen_dir}\chrome\browser\resources\chromeos\parent_access\flows\extension_approvals_before.js" use_base_dir="false" type="BINDATA" /> <include name="IDR_PARENT_ACCESS_CONTROLLER_JS" file="resources\chromeos\parent_access\parent_access_controller.js" type="BINDATA" /> <include name="IDR_PARENT_ACCESS_UI_HANDLER_JS" file="resources\chromeos\parent_access\parent_access_ui_handler.js" type="BINDATA" /> <include name="IDR_PARENT_ACCESS_SCREEN_JS" file="resources\chromeos\parent_access\parent_access_screen.js" type="BINDATA" />
diff --git a/chrome/browser/chooser_controller/title_util.cc b/chrome/browser/chooser_controller/title_util.cc index 4c0a7bc..974b6db0 100644 --- a/chrome/browser/chooser_controller/title_util.cc +++ b/chrome/browser/chooser_controller/title_util.cc
@@ -4,58 +4,40 @@ #include "chrome/browser/chooser_controller/title_util.h" +#include <string> + #include "base/strings/utf_string_conversions.h" #include "chrome/browser/profiles/profile.h" -#include "components/permissions/chooser_title_util.h" +#include "chrome/browser/ui/url_identity.h" #include "content/public/browser/render_frame_host.h" -#include "extensions/buildflags/buildflags.h" #include "ui/base/l10n/l10n_util.h" -#include "url/origin.h" +#include "url/gurl.h" -#if BUILDFLAG(ENABLE_EXTENSIONS) -#include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" -#include "chrome/browser/ui/web_applications/app_browser_controller.h" -#include "extensions/browser/extension_registry.h" -#include "extensions/common/constants.h" -#endif +namespace { -std::u16string CreateExtensionAwareChooserTitle( - content::RenderFrameHost* render_frame_host, - int title_string_id_origin, - int title_string_id_extension) { - if (!render_frame_host) +constexpr UrlIdentity::TypeSet kUrlIdentityAllowedTypes = { + UrlIdentity::Type::kDefault, UrlIdentity::Type::kFile, + UrlIdentity::Type::kIsolatedWebApp, UrlIdentity::Type::kChromeExtension}; +constexpr UrlIdentity::FormatOptions kUrlIdentityOptions{ + .default_options = { + UrlIdentity::DefaultFormatOptions::kOmitCryptographicScheme}}; + +} // namespace + +std::u16string CreateChooserTitle(content::RenderFrameHost* render_frame_host, + int title_string_id) { + if (!render_frame_host) { return u""; + } // Ensure the permission request is attributed to the main frame. render_frame_host = render_frame_host->GetMainFrame(); -#if BUILDFLAG(ENABLE_EXTENSIONS) - url::Origin origin = render_frame_host->GetLastCommittedOrigin(); + const GURL& url = render_frame_host->GetLastCommittedURL(); Profile* profile = Profile::FromBrowserContext(render_frame_host->GetBrowserContext()); - if (origin.scheme() == extensions::kExtensionScheme) { - if (auto* extension_registry = - extensions::ExtensionRegistry::Get(profile)) { - if (const extensions::Extension* extension = - extension_registry->enabled_extensions().GetByID(origin.host())) { - return l10n_util::GetStringFUTF16(title_string_id_extension, - base::UTF8ToUTF16(extension->name())); - } - } - } + UrlIdentity identity = UrlIdentity::CreateFromUrl( + profile, url, kUrlIdentityAllowedTypes, kUrlIdentityOptions); - // Isolated Web Apps should show the app's name instead of the origin. - Browser* browser = chrome::FindBrowserWithWebContents( - content::WebContents::FromRenderFrameHost(render_frame_host)); - if (browser && browser->app_controller() && - browser->app_controller()->IsIsolatedWebApp()) { - return l10n_util::GetStringFUTF16( - title_string_id_extension, - browser->app_controller()->GetAppShortName()); - } -#endif - - return permissions::CreateChooserTitle(render_frame_host, - title_string_id_origin); + return l10n_util::GetStringFUTF16(title_string_id, identity.name); }
diff --git a/chrome/browser/chooser_controller/title_util.h b/chrome/browser/chooser_controller/title_util.h index 0367e582..e3f5f6a 100644 --- a/chrome/browser/chooser_controller/title_util.h +++ b/chrome/browser/chooser_controller/title_util.h
@@ -11,12 +11,12 @@ class RenderFrameHost; } -// Creates a title for a chooser. For extensions the extension name is used if -// possible. In all other cases the origin of the main frame for +// Creates a title for a chooser. +// For Isolated Web Apps the app name is used if possible. +// For extensions the extension name is used if possible. +// In all other cases the origin of the main frame for // `render_frame_host` is used. -std::u16string CreateExtensionAwareChooserTitle( - content::RenderFrameHost* render_frame_host, - int title_string_id_origin, - int title_string_id_extension); +std::u16string CreateChooserTitle(content::RenderFrameHost* render_frame_host, + int title_string_id); #endif // CHROME_BROWSER_CHOOSER_CONTROLLER_TITLE_UTIL_H_
diff --git a/chrome/browser/chooser_controller/title_util_unittest.cc b/chrome/browser/chooser_controller/title_util_unittest.cc index 4df4d50..8e0a987 100644 --- a/chrome/browser/chooser_controller/title_util_unittest.cc +++ b/chrome/browser/chooser_controller/title_util_unittest.cc
@@ -8,36 +8,52 @@ #include "chrome/grit/generated_resources.h" #include "chrome/test/base/chrome_render_view_host_test_harness.h" #include "components/strings/grit/components_strings.h" +#include "content/public/test/navigation_simulator.h" +#include "url/gurl.h" +#include "url/origin.h" #if BUILDFLAG(ENABLE_EXTENSIONS) #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/test_extension_system.h" -#include "content/public/test/navigation_simulator.h" #include "extensions/browser/extension_registry.h" #include "extensions/common/extension.h" #include "extensions/common/extension_builder.h" #include "extensions/common/value_builder.h" -#include "url/gurl.h" -#include "url/origin.h" #endif // BUILDFLAG(ENABLE_EXTENSIONS) +#if !BUILDFLAG(IS_ANDROID) +#include "chrome/browser/ui/web_applications/test/isolated_web_app_test_utils.h" +#include "chrome/browser/web_applications/test/web_app_install_test_utils.h" +#endif // !BUILDFLAG(IS_ANDROID) + namespace { -constexpr int kNonExtensionTitleResourceId = - IDS_USB_DEVICE_CHOOSER_PROMPT_ORIGIN; -constexpr int kExtensionTitleResourceId = - IDS_USB_DEVICE_CHOOSER_PROMPT_EXTENSION_NAME; +constexpr int kTitleResourceId = IDS_USB_DEVICE_CHOOSER_PROMPT; -using ExtensionsAwareChooserTitleTest = ChromeRenderViewHostTestHarness; +using CreateChooserTitleTest = ChromeRenderViewHostTestHarness; -TEST_F(ExtensionsAwareChooserTitleTest, NoFrame) { - EXPECT_EQ(u"", CreateExtensionAwareChooserTitle(nullptr, - kNonExtensionTitleResourceId, - kExtensionTitleResourceId)); +TEST_F(CreateChooserTitleTest, NoFrame) { + EXPECT_EQ(u"", CreateChooserTitle(nullptr, kTitleResourceId)); +} + +TEST_F(CreateChooserTitleTest, UrlFrameTree) { + NavigateAndCommit(GURL("https://main-frame.com")); + content::RenderFrameHost* subframe = + content::NavigationSimulator::NavigateAndCommitFromDocument( + GURL("https://sub-frame.com"), + content::RenderFrameHostTester::For(main_rfh()) + ->AppendChild("subframe")); + + EXPECT_EQ("main-frame.com", main_rfh()->GetLastCommittedOrigin().host()); + EXPECT_EQ(u"main-frame.com wants to connect", + CreateChooserTitle(main_rfh(), kTitleResourceId)); + EXPECT_EQ("sub-frame.com", subframe->GetLastCommittedOrigin().host()); + EXPECT_EQ(u"main-frame.com wants to connect", + CreateChooserTitle(subframe, kTitleResourceId)); } #if BUILDFLAG(ENABLE_EXTENSIONS) -TEST_F(ExtensionsAwareChooserTitleTest, FrameTree) { +TEST_F(CreateChooserTitleTest, ExtensionsFrameTree) { extensions::DictionaryBuilder manifest; manifest.Set("name", "Chooser Title Subframe Test") .Set("version", "0.1") @@ -63,17 +79,39 @@ content::RenderFrameHostTester::For(main_rfh()) ->AppendChild("subframe")); - EXPECT_EQ(extension->id(), main_rfh()->GetLastCommittedOrigin().host()); - EXPECT_EQ( - u"\"Chooser Title Subframe Test\" wants to connect", - CreateExtensionAwareChooserTitle(main_rfh(), kNonExtensionTitleResourceId, - kExtensionTitleResourceId)); - EXPECT_NE(extension->id(), subframe->GetLastCommittedOrigin().host()); - EXPECT_EQ( - u"\"Chooser Title Subframe Test\" wants to connect", - CreateExtensionAwareChooserTitle(subframe, kNonExtensionTitleResourceId, - kExtensionTitleResourceId)); + ASSERT_EQ(extension->id(), main_rfh()->GetLastCommittedOrigin().host()); + EXPECT_EQ(u"Chooser Title Subframe Test wants to connect", + CreateChooserTitle(main_rfh(), kTitleResourceId)); + ASSERT_NE(extension->id(), subframe->GetLastCommittedOrigin().host()); + EXPECT_EQ(u"Chooser Title Subframe Test wants to connect", + CreateChooserTitle(subframe, kTitleResourceId)); } #endif // BUILDFLAG(ENABLE_EXTENSIONS) +#if !BUILDFLAG(IS_ANDROID) +TEST_F(CreateChooserTitleTest, IsolatedWebAppFrameTree) { + const GURL app_url( + "isolated-app://" + "berugqztij5biqquuk3mfwpsaibuegaqcitgfchwuosuofdjabzqaaic"); + const std::string app_name("Chooser Title FrameTree IWA Name"); + + web_app::test::AwaitStartWebAppProviderAndSubsystems(profile()); + web_app::AddDummyIsolatedAppToRegistry(profile(), app_url, app_name); + + NavigateAndCommit(app_url); + content::RenderFrameHost* subframe = + content::NavigationSimulator::NavigateAndCommitFromDocument( + GURL("data:text/html,"), + content::RenderFrameHostTester::For(main_rfh()) + ->AppendChild("subframe")); + + ASSERT_EQ(app_url, main_rfh()->GetLastCommittedOrigin().GetURL()); + EXPECT_EQ(u"Chooser Title FrameTree IWA Name wants to connect", + CreateChooserTitle(main_rfh(), kTitleResourceId)); + ASSERT_NE(app_url, subframe->GetLastCommittedOrigin().GetURL()); + EXPECT_EQ(u"Chooser Title FrameTree IWA Name wants to connect", + CreateChooserTitle(subframe, kTitleResourceId)); +} +#endif // !BUILDFLAG(IS_ANDROID) + } // namespace
diff --git a/chrome/browser/chromeos/extensions/printing_metrics/printing_metrics_api_unittest.cc b/chrome/browser/chromeos/extensions/printing_metrics/printing_metrics_api_unittest.cc index 6ed311d4..f2bca39 100644 --- a/chrome/browser/chromeos/extensions/printing_metrics/printing_metrics_api_unittest.cc +++ b/chrome/browser/chromeos/extensions/printing_metrics/printing_metrics_api_unittest.cc
@@ -138,7 +138,8 @@ ASSERT_TRUE(result->is_list()); ASSERT_EQ(1u, result->GetList().size()); std::unique_ptr<api::printing_metrics::PrintJobInfo> print_job_info = - api::printing_metrics::PrintJobInfo::FromValue(result->GetList()[0]); + api::printing_metrics::PrintJobInfo::FromValueDeprecated( + result->GetList()[0]); EXPECT_THAT( print_job_info, @@ -167,11 +168,13 @@ ASSERT_TRUE(result->is_list()); ASSERT_EQ(2u, result->GetList().size()); std::unique_ptr<api::printing_metrics::PrintJobInfo> print_job_info1 = - api::printing_metrics::PrintJobInfo::FromValue(result->GetList()[0]); + api::printing_metrics::PrintJobInfo::FromValueDeprecated( + result->GetList()[0]); EXPECT_TRUE(print_job_info1); EXPECT_EQ(kTitle1, print_job_info1->title); std::unique_ptr<api::printing_metrics::PrintJobInfo> print_job_info2 = - api::printing_metrics::PrintJobInfo::FromValue(result->GetList()[1]); + api::printing_metrics::PrintJobInfo::FromValueDeprecated( + result->GetList()[1]); EXPECT_TRUE(print_job_info2); EXPECT_EQ(kTitle2, print_job_info2->title); }
diff --git a/chrome/browser/creator/android/java/src/org/chromium/chrome/browser/creator/CreatorCoordinator.java b/chrome/browser/creator/android/java/src/org/chromium/chrome/browser/creator/CreatorCoordinator.java index 84976cd..2bb3665c 100644 --- a/chrome/browser/creator/android/java/src/org/chromium/chrome/browser/creator/CreatorCoordinator.java +++ b/chrome/browser/creator/android/java/src/org/chromium/chrome/browser/creator/CreatorCoordinator.java
@@ -309,6 +309,7 @@ view.setId(R.id.creator_feed_stream_recycler_view); view.setClipToPadding(false); view.setBackgroundColor(SemanticColorUtils.getDefaultBgColor(mActivity)); + view.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO); } else { view = null; }
diff --git a/chrome/browser/extensions/activity_log/activity_database.cc b/chrome/browser/extensions/activity_log/activity_database.cc index f529fd8..0a60882 100644 --- a/chrome/browser/extensions/activity_log/activity_database.cc +++ b/chrome/browser/extensions/activity_log/activity_database.cc
@@ -176,7 +176,7 @@ valid_db_ = false; timer_.Stop(); db_.reset_error_callback(); - db_.RazeAndClose(); + db_.RazeAndPoison(); delegate_->OnDatabaseFailure(); already_closed_ = true; }
diff --git a/chrome/browser/extensions/api/runtime/runtime_apitest.cc b/chrome/browser/extensions/api/runtime/runtime_apitest.cc index 844e984..fbbaf15 100644 --- a/chrome/browser/extensions/api/runtime/runtime_apitest.cc +++ b/chrome/browser/extensions/api/runtime/runtime_apitest.cc
@@ -6,6 +6,7 @@ #include "base/json/json_reader.h" #include "base/run_loop.h" +#include "base/strings/stringprintf.h" #include "base/test/bind.h" #include "base/test/simple_test_tick_clock.h" #include "base/test/values_test_util.h" @@ -635,17 +636,62 @@ // Runs `chrome.runtime.getContexts()` and returns the result as a // base::Value. - base::Value GetContexts() { - static constexpr char kScript[] = + base::Value GetContexts(const char* filter) { + static constexpr char kScriptTemplate[] = R"((async () => { chrome.test.sendScriptResult( - await chrome.runtime.getContexts()); + await chrome.runtime.getContexts(%s)); })();)"; + std::string script = base::StringPrintf(kScriptTemplate, filter); return BackgroundScriptExecutor::ExecuteScript( - profile(), extension_->id(), kScript, + profile(), extension_->id(), script, BackgroundScriptExecutor::ResultCapture::kSendScriptResult); } + // Runs `chrome.runtime.getContexts()` and returns the result as a vector + // of strongly-typed `ExtensionContext`s. Expects the getContexts() call to + // return a valid value (i.e., not throw an error). + std::vector<api::runtime::ExtensionContext> GetContextStructs( + const char* filter) { + base::Value value = GetContexts(filter); + if (!value.is_list()) { + ADD_FAILURE() << "Invalid return value: " << value; + return {}; + } + + std::vector<api::runtime::ExtensionContext> result; + result.reserve(value.GetList().size()); + for (const auto& entry : value.GetList()) { + if (!entry.is_dict()) { + ADD_FAILURE() << "Invalid return value: " << value; + return {}; + } + auto context = api::runtime::ExtensionContext::FromValue(entry.GetDict()); + if (!context) { + ADD_FAILURE() << "Invalid return value: " << value; + return {}; + } + + result.push_back(std::move(*context)); + } + + return result; + } + + // Returns a matcher that expects an ExtensionContext to be a valid + // background context (without testing details of the entry). + auto GetBackgroundMatcher() { + return testing::AllOf( + testing::Field(&api::runtime::ExtensionContext::context_type, + testing::Eq(api::runtime::CONTEXT_TYPE_BACKGROUND)), + testing::Field(&api::runtime::ExtensionContext::tab_id, + testing::Eq(-1)), + testing::Field(&api::runtime::ExtensionContext::window_id, + testing::Eq(-1)), + testing::Field(&api::runtime::ExtensionContext::frame_id, + testing::Eq(-1))); + } + private: const Extension* extension_ = nullptr; TestExtensionDir test_dir_; @@ -656,7 +702,10 @@ // Tests retrieving the background service worker context using // `chrome.runtime.getContexts()`. IN_PROC_BROWSER_TEST_F(RuntimeGetContextsApiTest, GetServiceWorkerContext) { - base::Value background_contexts = GetContexts(); + // An empty dictionary filter should match all contexts (of which there is + // only one). + base::Value contexts = GetContexts("{}"); + // Note: fields of `documentId`, `documentUrl`, and `documentOrigin` are // undefined (service worker contexts don't have an associated document). // `tabId`, `frameId`, and `windowId` are -1 for consistency with other @@ -670,10 +719,51 @@ "frameId": -1, "incognito": false }])"; - EXPECT_THAT(background_contexts, base::test::IsJson(kExpected)); + EXPECT_THAT(contexts, base::test::IsJson(kExpected)); // TODO(crbug/1426192): Add tests for retrieving a service worker context // when there isn't an active worker. } +// Tests the filter matching behavior of `runtime.getContexts()`. +IN_PROC_BROWSER_TEST_F(RuntimeGetContextsApiTest, FilterMatching) { + // Currently, there is only one context: the background service worker. + + { + // Passing a filter to match background contexts should match the worker. + std::vector<api::runtime::ExtensionContext> contexts = + GetContextStructs(R"({"contextTypes": ["BACKGROUND"]})"); + EXPECT_THAT(contexts, testing::ElementsAre(GetBackgroundMatcher())); + } + { + // Try passing a filter for a different context type. No contexts should + // match. + std::vector<api::runtime::ExtensionContext> contexts = + GetContextStructs(R"({"contextTypes": ["POPUP"]})"); + EXPECT_THAT(contexts, testing::IsEmpty()); + } + { + // Filter properties support an array of options; if the context matches an + // entry in the array, it matches the filter for that property. Thus, + // passing both "BACKGROUND" and "POPUP" should match the service worker + // context. + std::vector<api::runtime::ExtensionContext> contexts = + GetContextStructs(R"({"contextTypes": ["BACKGROUND", "POPUP"]})"); + EXPECT_THAT(contexts, testing::ElementsAre(GetBackgroundMatcher())); + } + { + // All specified filter properties must match. Thus, if we look for a + // background context and also specify a tab ID, nothing should match + // (since the background context doesn't have an associated tab). + static constexpr char kFilter[] = + R"({ + "contextTypes": ["BACKGROUND"], + "tabIds": [2] + })"; + std::vector<api::runtime::ExtensionContext> contexts = + GetContextStructs(kFilter); + EXPECT_THAT(contexts, testing::IsEmpty()); + } +} + } // namespace extensions
diff --git a/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc b/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc index f1628be..a2e2aca 100644 --- a/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc +++ b/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc
@@ -26,6 +26,7 @@ #include "chrome/browser/extensions/extension_apitest.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/renderer_context_menu/render_view_context_menu_test_util.h" +#include "chrome/browser/ssl/https_upgrades_util.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_navigator_params.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" @@ -303,6 +304,11 @@ IN_PROC_BROWSER_TEST_P(WebNavigationApiPrerenderTestWithContextType, Prerendering) { + // TODO(crbug.com/1394910): Use https in the test and remove this allowlist + // entry. + ScopedAllowHttpForHostnamesForTesting scoped_allow_http( + {"a.test"}, browser()->profile()->GetPrefs()); + ASSERT_TRUE(StartEmbeddedTestServer()); ASSERT_TRUE(RunExtensionTest("webnavigation/prerendering")) << message_; } @@ -369,6 +375,11 @@ IN_PROC_BROWSER_TEST_P(WebNavigationApiTestWithContextType, ServerRedirectSingleProcess) { + // TODO(crbug.com/1394910): Use https in the test and remove these allowlist + // entries. + ScopedAllowHttpForHostnamesForTesting scoped_allow_http( + {"www.a.com", "www.b.com"}, browser()->profile()->GetPrefs()); + ASSERT_TRUE(StartEmbeddedTestServer()); // Set max renderers to 1 to force running out of processes. @@ -674,6 +685,11 @@ } IN_PROC_BROWSER_TEST_P(WebNavigationApiTestWithContextType, Crash) { + // TODO(crbug.com/1394910): Use https in the test and remove this allowlist + // entry. + ScopedAllowHttpForHostnamesForTesting scoped_allow_http( + {"www.a.com"}, browser()->profile()->GetPrefs()); + content::ScopedAllowRendererCrashes scoped_allow_renderer_crashes; ASSERT_TRUE(StartEmbeddedTestServer());
diff --git a/chrome/browser/extensions/content_script_apitest.cc b/chrome/browser/extensions/content_script_apitest.cc index 2966e44cd..6f62be03 100644 --- a/chrome/browser/extensions/content_script_apitest.cc +++ b/chrome/browser/extensions/content_script_apitest.cc
@@ -23,6 +23,7 @@ #include "chrome/browser/extensions/identifiability_metrics_test_util.h" #include "chrome/browser/search/search.h" #include "chrome/browser/ssl/https_upgrades_interceptor.h" +#include "chrome/browser/ssl/https_upgrades_util.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_navigator.h" #include "chrome/browser/ui/search/ntp_test_utils.h" @@ -145,6 +146,19 @@ https_test_server_->port()); HttpsUpgradesInterceptor::SetHttpPortForTesting( embedded_test_server()->port()); + + // Test extensions use these hostnames. Allow them to be loaded over + // HTTP so that HTTPS-Upgrades feature doesn't upgrade their URLs. + // TODO(crbug.com/1394910): Use https in these tests and remove these + // allowlist entries. + AllowHttpForHostnamesForTesting( + {"a.com", "b.com", "default.test", "bar.com", "path-test.example", + "example.com", "chromium.org", "example1.com"}, + browser()->profile()->GetPrefs()); + } + + void TearDownOnMainThread() override { + ClearHttpAllowlistForHostnamesForTesting(browser()->profile()->GetPrefs()); } void SetUpCommandLine(base::CommandLine* command_line) override { @@ -994,13 +1008,8 @@ // The extension should inject on "normal" urls. - // Test on an HTTP URL. Disable HTTPS upgrades on example1.com for this test - // to work. - auto* prefs = browser()->profile()->GetPrefs(); - base::Value::List allowlist; - allowlist.Append("example1.com"); - prefs->SetList(prefs::kHttpAllowlist, std::move(allowlist)); - + // Test on an HTTP URL. HTTPS upgrades is disabled on example1.com so it + // loads over http instead of https. example2.com loads over https. GURL unprotected_url1 = embedded_test_server()->GetURL( "example1.com", "/extensions/test_file.html"); ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), unprotected_url1));
diff --git a/chrome/browser/extensions/device_permissions_dialog_controller.cc b/chrome/browser/extensions/device_permissions_dialog_controller.cc index e4d6799..5414e8c 100644 --- a/chrome/browser/extensions/device_permissions_dialog_controller.cc +++ b/chrome/browser/extensions/device_permissions_dialog_controller.cc
@@ -13,11 +13,9 @@ DevicePermissionsDialogController::DevicePermissionsDialogController( content::RenderFrameHost* owner, scoped_refptr<extensions::DevicePermissionsPrompt::Prompt> prompt) - : ChooserController(CreateExtensionAwareChooserTitle( + : ChooserController(CreateChooserTitle( owner, prompt->multiple() ? IDS_DEVICE_PERMISSIONS_PROMPT_MULTIPLE_SELECTION - : IDS_DEVICE_PERMISSIONS_PROMPT_SINGLE_SELECTION, - prompt->multiple() ? IDS_DEVICE_PERMISSIONS_PROMPT_MULTIPLE_SELECTION : IDS_DEVICE_PERMISSIONS_PROMPT_SINGLE_SELECTION)), prompt_(prompt) { prompt_->SetObserver(this);
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json index ef165e6..bcc31002 100644 --- a/chrome/browser/flag-metadata.json +++ b/chrome/browser/flag-metadata.json
@@ -3214,11 +3214,6 @@ "expiry_milestone": 114 }, { - "name": "enable-tab-strip-improvements", - "owners": [ "skavuluru", "clank-app-team@google.com" ], - "expiry_milestone": 114 - }, - { "name": "enable-tab-strip-redesign", "owners": [ "zheliooo", "skavuluru", "nemco", "clank-app-team@google.com" ], "expiry_milestone": 120 @@ -5329,6 +5324,11 @@ "expiry_milestone": 120 }, { + "name": "omnibox-gm3-steady-state-text-color", + "owners": [ "khalidpeer", "manukh", "chrome-omnibox-team@google.com" ], + "expiry_milestone": 120 + }, + { "name": "omnibox-gm3-steady-state-text-style", "owners": [ "khalidpeer", "manukh", "chrome-omnibox-team@google.com" ], "expiry_milestone": 120
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc index 37ab5f81..910c2eb 100644 --- a/chrome/browser/flag_descriptions.cc +++ b/chrome/browser/flag_descriptions.cc
@@ -2112,6 +2112,11 @@ const char kOmniboxGM3SteadyStateTextStyleDescription[] = "Updates Omnibox steady state text style to comply with GM3 guidelines."; +const char kOmniboxGM3SteadyStateTextColorName[] = + "Omnibox Steady State Text Color"; +const char kOmniboxGM3SteadyStateTextColorDescription[] = + "Updates Omnibox steady state text color to comply with GM3 guidelines."; + const char kOmniboxGroupingFrameworkZPSName[] = "Omnibox Grouping Framework for ZPS"; const char kOmniboxGroupingFrameworkNonZPSName[] = @@ -4142,11 +4147,6 @@ const char kTabGroupsForTabletsName[] = "Tab groups on tablets"; const char kTabGroupsForTabletsDescription[] = "Enable tab groups on tablets."; -const char kTabStripImprovementsAndroidName[] = - "Tab strip improvements for Android."; -const char kTabStripImprovementsAndroidDescription[] = - "Enables scrollable tab strip with tab group indicators."; - const char kDiscoverFeedMultiColumnAndroidName[] = "Multi-column Discover feed Android."; const char kDiscoverFeedMultiColumnAndroidDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h index 332a2bb..f37a49e 100644 --- a/chrome/browser/flag_descriptions.h +++ b/chrome/browser/flag_descriptions.h
@@ -1187,6 +1187,9 @@ extern const char kOmniboxGM3SteadyStateTextStyleName[]; extern const char kOmniboxGM3SteadyStateTextStyleDescription[]; +extern const char kOmniboxGM3SteadyStateTextColorName[]; +extern const char kOmniboxGM3SteadyStateTextColorDescription[]; + extern const char kOmniboxGroupingFrameworkNonZPSName[]; extern const char kOmniboxGroupingFrameworkZPSName[]; extern const char kOmniboxGroupingFrameworkDescription[]; @@ -1734,9 +1737,6 @@ extern const char kTabSearchFuzzySearchName[]; extern const char kTabSearchFuzzySearchDescription[]; -extern const char kTabStripImprovementsAndroidName[]; -extern const char kTabStripImprovementsAndroidDescription[]; - extern const char kDiscoverFeedMultiColumnAndroidName[]; extern const char kDiscoverFeedMultiColumnAndroidDescription[];
diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc index 893a017..8fbd6862 100644 --- a/chrome/browser/flags/android/chrome_feature_list.cc +++ b/chrome/browser/flags/android/chrome_feature_list.cc
@@ -304,7 +304,6 @@ &kTabGridLayoutAndroid, &kTabSelectionEditorV2, &kTabStateV1Optimizations, - &kTabStripImprovements, &kTabToGTSAnimation, &kTestDefaultDisabled, &kTestDefaultEnabled, @@ -1002,10 +1001,6 @@ "TabStateV1Optimizations", base::FEATURE_ENABLED_BY_DEFAULT); -BASE_FEATURE(kTabStripImprovements, - "TabStripImprovements", - base::FEATURE_ENABLED_BY_DEFAULT); - BASE_FEATURE(kDiscoverFeedMultiColumn, "DiscoverFeedMultiColumn", base::FEATURE_DISABLED_BY_DEFAULT);
diff --git a/chrome/browser/flags/android/chrome_feature_list.h b/chrome/browser/flags/android/chrome_feature_list.h index 3e90bb1..6c7f7ef 100644 --- a/chrome/browser/flags/android/chrome_feature_list.h +++ b/chrome/browser/flags/android/chrome_feature_list.h
@@ -168,7 +168,6 @@ BASE_DECLARE_FEATURE(kTabGridLayoutAndroid); BASE_DECLARE_FEATURE(kTabSelectionEditorV2); BASE_DECLARE_FEATURE(kTabStateV1Optimizations); -BASE_DECLARE_FEATURE(kTabStripImprovements); BASE_DECLARE_FEATURE(kDiscoverFeedMultiColumn); BASE_DECLARE_FEATURE(kTabStripRedesign); BASE_DECLARE_FEATURE(kTabToGTSAnimation);
diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java index 94d0e79..74b00338 100644 --- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java +++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
@@ -198,8 +198,6 @@ "AutofillEnableNewCardArtAndNetworkImages"; public static final String AUTOFILL_ENABLE_SUPPORT_FOR_HONORIFIC_PREFIXES = "AutofillEnableSupportForHonorificPrefixes"; - public static final String AUTOFILL_ENABLE_SUPPORT_FOR_MORE_STRUCTURE_IN_ADDRESSES = - "AutofillEnableSupportForMoreStructureInAddresses"; public static final String AUTOFILL_ENABLE_UPDATE_VIRTUAL_CARD_ENROLLMENT = "AutofillEnableUpdateVirtualCardEnrollment"; public static final String AUTOFILL_ENABLE_VIRTUAL_CARD_METADATA = @@ -215,8 +213,6 @@ public static final String BACK_GESTURE_REFACTOR_ACTIVITY = "BackGestureRefactorActivityAndroid"; public static final String BASELINE_GM3_SURFACE_COLORS = "BaselineGM3SurfaceColors"; - public static final String BIOMETRIC_TOUCH_TO_FILL = "BiometricTouchToFill"; - public static final String BOOKMARKS_IMPROVED_SAVE_FLOW = "BookmarksImprovedSaveFlow"; public static final String BOOKMARKS_REFRESH = "BookmarksRefresh"; public static final String CACHE_DEPRECATED_SYSTEM_LOCATION_SETTING = "CacheDeprecatedSystemLocationSetting"; @@ -225,7 +221,6 @@ public static final String CCT_ALLOW_CROSS_UID_ACTIVITY_SWITCH_FROM_BELOW = "CCTAllowCrossUidActivitySwitchFromBelow"; public static final String CCT_AUTO_TRANSLATE = "CCTAutoTranslate"; - public static final String CCT_BACKGROUND_TAB = "CCTBackgroundTab"; public static final String CCT_BOTTOM_BAR_SWIPE_UP_GESTURE = "CCTBottomBarSwipeUpGesture"; public static final String CCT_BRAND_TRANSPARENCY = "CCTBrandTransparency"; public static final String CCT_CLIENT_DATA_HEADER = "CCTClientDataHeader"; @@ -257,7 +252,6 @@ public static final String CCT_RESOURCE_PREFETCH = "CCTResourcePrefetch"; public static final String CCT_RETAINING_STATE_IN_MEMORY = "CCTRetainingStateInMemory"; public static final String CCT_TOOLBAR_CUSTOMIZATIONS = "CCTToolbarCustomizations"; - public static final String CHROME_NEW_DOWNLOAD_TAB = "ChromeNewDownloadTab"; public static final String CHROME_SHARING_HUB = "ChromeSharingHub"; public static final String CHROME_SHARING_HUB_LAUNCH_ADJACENT = "ChromeSharingHubLaunchAdjacent"; @@ -269,7 +263,6 @@ public static final String COMMERCE_COUPONS = "CommerceCoupons"; public static final String COMMERCE_MERCHANT_VIEWER = "CommerceMerchantViewer"; public static final String COMMERCE_PRICE_TRACKING = "CommercePriceTracking"; - public static final String CONTACTS_PICKER_SELECT_ALL = "ContactsPickerSelectAll"; public static final String CONTEXTUAL_PAGE_ACTIONS = "ContextualPageActions"; public static final String CONTEXTUAL_PAGE_ACTION_PRICE_TRACKING = "ContextualPageActionPriceTracking"; @@ -304,13 +297,10 @@ public static final String DETAILED_LANGUAGE_SETTINGS = "DetailedLanguageSettings"; public static final String DISCO_FEED_ENDPOINT = "DiscoFeedEndpoint"; public static final String DNS_OVER_HTTPS = "DnsOverHttps"; - public static final String DONT_AUTO_HIDE_BROWSER_CONTROLS = "DontAutoHideBrowserControls"; - public static final String DOWNLOADS_FOREGROUND = "DownloadsForeground"; public static final String DOWNLOAD_OFFLINE_CONTENT_PROVIDER = "UseDownloadOfflineContentProvider"; public static final String EARLY_LIBRARY_LOAD = "EarlyLibraryLoad"; public static final String ENABLE_IPH = "EnableIPH"; - public static final String ENABLE_PASSWORDS_ACCOUNT_STORAGE = "EnablePasswordsAccountStorage"; public static final String EXPERIMENTS_FOR_AGSA = "ExperimentsForAgsa"; public static final String EXPLICIT_LANGUAGE_ASK = "ExplicitLanguageAsk"; public static final String EXPLORE_SITES = "ExploreSites"; @@ -321,14 +311,10 @@ "FeedImageMemoryCacheSizePercentage"; public static final String FEED_LOADING_PLACEHOLDER = "FeedLoadingPlaceholder"; public static final String FEED_MULTI_COLUMN = "DiscoverFeedMultiColumn"; - public static final String FEED_NO_VIEW_CACHE = "FeedNoViewCache"; - public static final String FEED_INTERACTIVE_REFRESH = "FeedInteractiveRefresh"; - public static final String FEED_PERFORMANCE_STUDY = "FeedPerformanceStudy"; public static final String FEED_POSITION_ANDROID = "FeedPositionAndroid"; public static final String FEED_SHOW_SIGN_IN_COMMAND = "FeedShowSignInCommand"; public static final String FEED_BOC_SIGN_IN_INTERSTITIAL = "FeedBoCSigninInterstitial"; public static final String FILLING_PASSWORDS_FROM_ANY_ORIGIN = "FillingPasswordsFromAnyOrigin"; - public static final String FIXED_UMA_SESSION_RESUME_ORDER = "FixedUmaSessionResumeOrder"; public static final String FOCUS_OMNIBOX_IN_INCOGNITO_TAB_INTENTS = "FocusOmniboxInIncognitoTabIntents"; public static final String FOLDABLE_JANK_FIX = "FoldableJankFix"; @@ -354,8 +340,6 @@ public static final String INSTANCE_SWITCHER = "InstanceSwitcher"; public static final String INSTANT_START = "InstantStart"; public static final String INTEREST_FEED_CONTENT_SUGGESTIONS = "InterestFeedContentSuggestions"; - public static final String INTEREST_FEED_NOTICE_CARD_AUTO_DISMISS = - "InterestFeedNoticeCardAutoDismiss"; public static final String INTEREST_FEED_V2 = "InterestFeedV2"; public static final String INTEREST_FEED_V2_AUTOPLAY = "InterestFeedV2Autoplay"; public static final String INTEREST_FEED_V2_HEARTS = "InterestFeedV2Hearts"; @@ -366,17 +350,12 @@ public static final String LOCAL_WEB_APPROVALS = "LocalWebApprovals"; public static final String LOOKALIKE_NAVIGATION_URL_SUGGESTIONS_UI = "LookalikeUrlNavigationSuggestionsUI"; - public static final String MARK_HTTP_AS = "MarkHttpAs"; public static final String MESSAGES_FOR_ANDROID_ADS_BLOCKED = "MessagesForAndroidAdsBlocked"; public static final String MESSAGES_FOR_ANDROID_INFRASTRUCTURE = "MessagesForAndroidInfrastructure"; public static final String MESSAGES_FOR_ANDROID_PERMISSION_UPDATE = "MessagesForAndroidPermissionUpdate"; - public static final String MESSAGES_FOR_ANDROID_STACKING_ANIMATION = - "MessagesForAndroidStackingAnimation"; - public static final String MESSAGES_FOR_ANDROID_SYNC_ERROR = "MessagesForAndroidSyncError"; public static final String METRICS_SETTINGS_ANDROID = "MetricsSettingsAndroid"; - public static final String MODAL_PERMISSION_DIALOG_VIEW = "ModalPermissionDialogView"; public static final String NOTIFICATION_PERMISSION_VARIANT = "NotificationPermissionVariant"; public static final String NOTIFICATION_PERMISSION_BOTTOM_SHEET = "NotificationPermissionBottomSheet"; @@ -406,7 +385,6 @@ "OptimizationGuidePushNotifications"; public static final String OPTIMIZE_GEOLOCATION_HEADER_GENERATION = "OptimizeGeolocationHeaderGeneration"; - public static final String OVERLAY_NEW_LAYOUT = "OverlayNewLayout"; public static final String PAGE_ANNOTATIONS_SERVICE = "PageAnnotationsService"; public static final String PAGE_INFO_ABOUT_THIS_SITE_EN = "PageInfoAboutThisSiteEn"; public static final String PAGE_INFO_ABOUT_THIS_SITE_IMPROVED_BOTTOMSHEET = @@ -416,15 +394,11 @@ public static final String PAGE_INFO_ABOUT_THIS_SITE_NEW_ICON = "PageInfoAboutThisSiteNewIcon"; public static final String PAGE_INFO_ABOUT_THIS_SITE_NON_EN = "PageInfoAboutThisSiteNonEn"; public static final String PAINT_PREVIEW_DEMO = "PaintPreviewDemo"; - public static final String PAINT_PREVIEW_SHOW_ON_STARTUP = "PaintPreviewShowOnStartup"; public static final String PARTNER_HOMEPAGE_INITIAL_LOAD_IMPROVEMENT = "PartnerHomepageInitialLoadImprovement"; public static final String PASSKEY_MANAGEMENT_USING_ACCOUNT_SETTINGS_ANDROID = "PasskeyManagementUsingAccountSettingsAndroid"; - public static final String PASSWORD_DOMAIN_CAPABILITIES_FETCHING = - "PasswordDomainCapabilitiesFetching"; public static final String PASSWORD_EDIT_DIALOG_WITH_DETAILS = "PasswordEditDialogWithDetails"; - public static final String PERMISSION_DELEGATION = "PermissionDelegation"; public static final String PORTALS = "Portals"; public static final String PORTALS_CROSS_ORIGIN = "PortalsCrossOrigin"; public static final String POST_TASK_FOCUS_TAB = "PostTaskFocusTab"; @@ -457,8 +431,6 @@ public static final String RECOVER_FROM_NEVER_SAVE_ANDROID = "RecoverFromNeverSaveAndroid"; public static final String REENGAGEMENT_NOTIFICATION = "ReengagementNotification"; public static final String RELATED_SEARCHES = "RelatedSearches"; - public static final String REPORT_PARENTAL_CONTROL_SITES_CHILD = - "ReportParentalControlSitesChild"; public static final String REQUEST_DESKTOP_SITE_DEFAULTS = "RequestDesktopSiteDefaults"; public static final String REQUEST_DESKTOP_SITE_DEFAULTS_CONTROL = "RequestDesktopSiteDefaultsControl"; @@ -486,10 +458,7 @@ public static final String SEND_TAB_TO_SELF_SIGNIN_PROMO = "SendTabToSelfSigninPromo"; public static final String SEND_TAB_TO_SELF_V2 = "SendTabToSelfV2"; public static final String SHARED_HIGHLIGHTING_AMP = "SharedHighlightingAmp"; - public static final String SHARE_CROW_BUTTON = "ShareCrowButton"; - public static final String SHARE_CROW_BUTTON_LAUNCH_TAB = "ShareCrowLaunchTab"; public static final String SHOPPING_LIST = "ShoppingList"; - public static final String SHOW_EXTENDED_PRELOADING_SETTING = "ShowExtendedPreloadingSetting"; public static final String SHOW_SCROLLABLE_MVT_ON_NTP_ANDROID = "ShowScrollableMVTOnNTPAndroid"; public static final String SKIP_SERVICE_WORKER_FOR_INSTALL_PROMPT = "SkipServiceWorkerForInstallPromot"; @@ -520,7 +489,6 @@ public static final String TAB_GROUPS_FOR_TABLETS = "TabGroupsForTablets"; public static final String TAB_SELECTION_EDITOR_V2 = "TabSelectionEditorV2"; public static final String TAB_STATE_V1_OPTIMIZATIONS = "TabStateV1Optimizations"; - public static final String TAB_STRIP_IMPROVEMENTS = "TabStripImprovements"; public static final String TAB_STRIP_REDESIGN = "TabStripRedesign"; public static final String TAB_TO_GTS_ANIMATION = "TabToGTSAnimation"; public static final String TANGIBLE_SYNC = "TangibleSync"; @@ -541,18 +509,13 @@ "TrustedWebActivityQualityEnforcementForced"; public static final String TRUSTED_WEB_ACTIVITY_QUALITY_ENFORCEMENT_WARNING = "TrustedWebActivityQualityEnforcementWarning"; - public static final String UNIFIED_CREDENTIAL_MANAGER_DRY_RUN = - "UnifiedCredentialManagerDryRun"; public static final String UNIFIED_PASSWORD_MANAGER_ANDROID = "UnifiedPasswordManagerAndroid"; public static final String UNIFIED_PASSWORD_MANAGER_ANDROID_BRANDING = "UnifiedPasswordManagerAndroidBranding"; public static final String UNIFIED_PASSWORD_MANAGER_ERROR_MESSAGES = "UnifiedPasswordManagerErrorMessages"; public static final String UPCOMING_SHARING_FEATURES = "UpcomingSharingFeatures"; - public static final String UPDATE_NOTIFICATION_IMMEDIATE_SHOW_OPTION = - "UpdateNotificationScheduleServiceImmediateShowOption"; public static final String USE_CHIME_ANDROID_SDK = "UseChimeAndroidSdk"; - public static final String USE_CLIENT_CONFIG_IPH = "UseClientConfigIPH"; public static final String USE_LIBUNWINDSTACK_NATIVE_UNWINDER_ANDROID = "UseLibunwindstackNativeUnwinderAndroid"; public static final String VIDEO_TUTORIALS = "VideoTutorials"; @@ -560,8 +523,6 @@ public static final String VOICE_SEARCH_AUDIO_CAPTURE_POLICY = "VoiceSearchAudioCapturePolicy"; public static final String WEBNOTES_STYLIZE = "WebNotesStylize"; public static final String WEB_APK_ALLOW_ICON_UPDATA = "WebApkAllowIconUpdate"; - public static final String WEB_APK_INSTALL_COMPLETE_NOTIFICATION = - "WebApkInstallCompleteNotification"; public static final String WEB_APK_INSTALL_SERVICE = "WebApkInstallService"; public static final String WEB_APK_TRAMPOLINE_ON_INITIAL_INTENT = "WebApkTrampolineOnInitialIntent"; @@ -682,8 +643,6 @@ new CachedFlag(TAB_GROUPS_FOR_TABLETS, true); public static final CachedFlag sTabSelectionEditorV2 = new CachedFlag(TAB_SELECTION_EDITOR_V2, false); - public static final CachedFlag sTabStripImprovements = - new CachedFlag(TAB_STRIP_IMPROVEMENTS, true); public static final CachedFlag sTabStripRedesign = new CachedFlag(TAB_STRIP_REDESIGN, false); public static final CachedFlag sTabToGTSAnimation = new CachedFlag(TAB_TO_GTS_ANIMATION, true); public static final CachedFlag sTestDefaultDisabled =
diff --git a/chrome/browser/resources/chromeos/parent_access/BUILD.gn b/chrome/browser/resources/chromeos/parent_access/BUILD.gn index f8e8645..52667156 100644 --- a/chrome/browser/resources/chromeos/parent_access/BUILD.gn +++ b/chrome/browser/resources/chromeos/parent_access/BUILD.gn
@@ -26,7 +26,9 @@ ":parent_access_screen", ":parent_access_ui", ":parent_access_ui_handler", + ":utils", ":webview_manager", + "flows:extension_approvals_before", "flows:extension_approvals_disabled", "flows:local_web_approvals_after", ] @@ -108,6 +110,9 @@ js_library("parent_access_screen") { } +js_library("utils") { +} + html_to_js("web_components") { js_files = [ "parent_access_after.js",
diff --git a/chrome/browser/resources/chromeos/parent_access/flows/BUILD.gn b/chrome/browser/resources/chromeos/parent_access/flows/BUILD.gn index e65d9a5..82257676 100644 --- a/chrome/browser/resources/chromeos/parent_access/flows/BUILD.gn +++ b/chrome/browser/resources/chromeos/parent_access/flows/BUILD.gn
@@ -5,6 +5,14 @@ import("//third_party/closure_compiler/compile_js.gni") import("//tools/polymer/html_to_js.gni") +js_library("extension_approvals_before") { + deps = [ + "//ash/webui/common/resources:i18n_behavior", + "//chrome/browser/ui/webui/ash/parent_access:mojo_bindings_webui_js", + "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled", + ] +} + js_library("extension_approvals_disabled") { deps = [ "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled", @@ -21,6 +29,7 @@ html_to_js("web_components") { js_files = [ + "extension_approvals_before.js", "extension_approvals_disabled.js", "local_web_approvals_after.js", ]
diff --git a/chrome/browser/resources/chromeos/parent_access/flows/extension_approvals_before.html b/chrome/browser/resources/chromeos/parent_access/flows/extension_approvals_before.html new file mode 100644 index 0000000..21eacb0 --- /dev/null +++ b/chrome/browser/resources/chromeos/parent_access/flows/extension_approvals_before.html
@@ -0,0 +1,50 @@ +<style> + #before-screen-content { + color: var(--cros-color-primary); + display: flex; + flex-direction: column; + } + + #title { + font-family: var(--cros-font-family-google-sans); + font-size: 24px; + font-weight: 500; + margin-bottom: 0; + margin-top: 16px; + } + + #subtitle { + align-items: center; + color: var(--cros-color-secondary); + display: flex; + font: var(--cros-body-1-font); + margin-top: 16px; + } + + #extension-title { + align-items: center; + display: flex; + margin-top: 24px; + } + + #icon { + height: 32px; + margin-inline-end: 16px; + width: 32px; + } + + #extension-name { + font-family: var(--cros-font-family-google-sans); + font-size: 16px; + font-weight: 500; + } +</style> + +<div id="before-screen-content"> + <h2 id="title">[[title]]</h2> + <div id="subtitle">[[subtitle]]</div> + <div id="extension-title"> + <img id="icon" src="[[extensionIconSrc]]" alt=""></img> + <div id="extension-name">[[extensionName]]</div> + </div> +</div>
diff --git a/chrome/browser/resources/chromeos/parent_access/flows/extension_approvals_before.js b/chrome/browser/resources/chromeos/parent_access/flows/extension_approvals_before.js new file mode 100644 index 0000000..a57e536 --- /dev/null +++ b/chrome/browser/resources/chromeos/parent_access/flows/extension_approvals_before.js
@@ -0,0 +1,100 @@ +// 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. + +import {I18nBehavior, I18nBehaviorInterface} from 'chrome://resources/ash/common/i18n_behavior.js'; +import {html, mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; + +import {ExtensionApprovalsParams_ExtensionApprovalType} from '../parent_access_ui.mojom-webui.js'; +import {getParentAccessParams} from '../parent_access_ui_handler.js'; +import {decodeMojoString16, getBase64EncodedSrcForPng} from '../utils.js'; + +/** + * @constructor + * @extends {PolymerElement} + * @implements {I18nBehaviorInterface} + */ +const ExtensionApprovalsBeforeBase = + mixinBehaviors([I18nBehavior], PolymerElement); + +export class ExtensionApprovalsBefore extends ExtensionApprovalsBeforeBase { + static get is() { + return 'extension-approvals-before'; + } + + static get template() { + return html`{__html_template__}`; + } + + static get properties() { + return { + title: {type: String}, + subtitle: {type: String}, + extensionIconSrc: {type: String}, + extensionName: {type: String}, + }; + } + + constructor() { + super(); + /** + * Title of the screen shown to the user. + * @protected {string} + */ + this.title = ''; + /** + * Subtitle of the screen shown to the user. + * @protected {string} + */ + this.subtitle = ''; + /** + * The extension icon, represented as a Base64 encoded + * string. + * @protected {string} + */ + this.extensionIconSrc = ''; + /** + * Display name of the extension. + * @protected {string} + */ + this.extensionName = ''; + } + + /** @override */ + ready() { + super.ready(); + this.configureWithParams_(); + } + + /** @private */ + async configureWithParams_() { + const response = await getParentAccessParams(); + const params = response.params.flowTypeParams.extensionApprovalsParams; + if (params) { + this.renderDetails_(params); + } else { + console.error('Failed to fetch extension approvals params.'); + } + } + + /** @private */ + renderDetails_(params) { + this.extensionIconSrc = getBase64EncodedSrcForPng(params.iconPngBytes); + this.extensionName = decodeMojoString16(params.extensionName); + + switch (params.approvalType) { + case ExtensionApprovalsParams_ExtensionApprovalType.kAdd: + this.title = this.i18n('extensionApprovalsAddExtensionBeforeTitle'); + this.subtitle = + this.i18n('extensionApprovalsAddExtensionBeforeSubtitle'); + break; + case ExtensionApprovalsParams_ExtensionApprovalType.kEnable: + this.title = this.i18n('extensionApprovalsEnableExtensionBeforeTitle'); + this.subtitle = + this.i18n('extensionApprovalsEnableExtensionBeforeSubtitle'); + break; + } + } +} + +customElements.define(ExtensionApprovalsBefore.is, ExtensionApprovalsBefore);
diff --git a/chrome/browser/resources/chromeos/parent_access/flows/local_web_approvals_after.js b/chrome/browser/resources/chromeos/parent_access/flows/local_web_approvals_after.js index ac62af04..3cba011e 100644 --- a/chrome/browser/resources/chromeos/parent_access/flows/local_web_approvals_after.js +++ b/chrome/browser/resources/chromeos/parent_access/flows/local_web_approvals_after.js
@@ -5,11 +5,11 @@ import 'chrome://resources/cr_elements/cr_shared_vars.css.js'; import {I18nBehavior, I18nBehaviorInterface} from 'chrome://resources/ash/common/i18n_behavior.js'; -import {String16} from 'chrome://resources/mojo/mojo/public/mojom/base/string16.mojom-webui.js'; import {html, mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; import {WebApprovalsParams} from '../parent_access_ui.mojom-webui.js'; import {getParentAccessParams} from '../parent_access_ui_handler.js'; +import {decodeMojoString16, getBase64EncodedSrcForPng} from '../utils.js'; /** * @constructor @@ -79,19 +79,9 @@ * @private */ renderDetails_(params) { - this.childName = this.decodeMojoString16_(params.childDisplayName); + this.childName = decodeMojoString16(params.childDisplayName); this.url = params.url.url; - // Convert the PNG bytes to a Base64 encoded string. - const favicon = btoa(String.fromCharCode(...params.faviconPngBytes)); - this.favicon = 'data:image/png;base64,' + favicon; - } - - /** - * @param {!String16} str - * @private - */ - decodeMojoString16_(str) { - return str.data.map((ch) => String.fromCodePoint(ch)).join(''); + this.favicon = getBase64EncodedSrcForPng(params.faviconPngBytes); } }
diff --git a/chrome/browser/resources/chromeos/parent_access/parent_access_before.html b/chrome/browser/resources/chromeos/parent_access/parent_access_before.html index 3fed7535..493eac7 100644 --- a/chrome/browser/resources/chromeos/parent_access/parent_access_before.html +++ b/chrome/browser/resources/chromeos/parent_access/parent_access_before.html
@@ -33,7 +33,7 @@ media="(prefers-color-scheme: dark)"> <img src="images/request_approval.svg" id="illustration"> </picture> - <div id="befpre-screen-body" aria-live="polite"></div> + <div id="before-screen-body" aria-live="polite"></div> <div id="before-screen-buttons"> <cr-button class="action-button" on-click="showParentAccessUI_"> $i18n{askInPersonButtonText}
diff --git a/chrome/browser/resources/chromeos/parent_access/parent_access_before.js b/chrome/browser/resources/chromeos/parent_access/parent_access_before.js index a27ffe8..1e5cfc8a 100644 --- a/chrome/browser/resources/chromeos/parent_access/parent_access_before.js +++ b/chrome/browser/resources/chromeos/parent_access/parent_access_before.js
@@ -6,7 +6,10 @@ import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; +import {ExtensionApprovalsBefore} from './flows/extension_approvals_before.js'; import {ParentAccessScreenInterface} from './parent_access_screen.js'; +import {ParentAccessParams_FlowType} from './parent_access_ui.mojom-webui.js'; +import {getParentAccessParams} from './parent_access_ui_handler.js'; /** @implements {ParentAccessScreenInterface} */ class ParentAccessBefore extends PolymerElement { @@ -30,7 +33,15 @@ /** @override */ async renderFlowSpecificContent() { - // TODO(b/262448127): Implement flow specific content. + const response = await getParentAccessParams(); + switch (response.params.flowType) { + case ParentAccessParams_FlowType.kExtensionAccess: + this.shadowRoot.querySelector('#before-screen-body') + .appendChild(new ExtensionApprovalsBefore()); + return; + default: + return; + } } /** @private */
diff --git a/chrome/browser/resources/chromeos/parent_access/utils.js b/chrome/browser/resources/chromeos/parent_access/utils.js new file mode 100644 index 0000000..0e456a5 --- /dev/null +++ b/chrome/browser/resources/chromeos/parent_access/utils.js
@@ -0,0 +1,22 @@ +// 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. + +import {String16} from 'chrome://resources/mojo/mojo/public/mojom/base/string16.mojom-webui.js'; + +/** + * @param {!String16} str + * @return {string} + */ +export function decodeMojoString16(str) { + return str.data.map((ch) => String.fromCodePoint(ch)).join(''); +} + +/** + * @param {!Array<number>} pngBytes + * @return {string} + */ +export function getBase64EncodedSrcForPng(pngBytes) { + const image = btoa(String.fromCharCode(...pngBytes)); + return 'data:image/png;base64,' + image; +}
diff --git a/chrome/browser/resources/settings/chromeos/os_settings_search_box/os_settings_search_box.html b/chrome/browser/resources/settings/chromeos/os_settings_search_box/os_settings_search_box.html index f855eca..571c32a75 100644 --- a/chrome/browser/resources/settings/chromeos/os_settings_search_box/os_settings_search_box.html +++ b/chrome/browser/resources/settings/chromeos/os_settings_search_box/os_settings_search_box.html
@@ -20,7 +20,7 @@ } :host-context(body.jelly-enabled):host { - --cr-toolbar-search-field-background: var(--cros-sys-input_field_dark); + --cr-toolbar-search-field-background: var(--cros-sys-input_field_on_base); } @media (prefers-color-scheme: dark) {
diff --git a/chrome/browser/ssl/https_only_mode_browsertest.cc b/chrome/browser/ssl/https_only_mode_browsertest.cc index 3aabdc63..d2051a19 100644 --- a/chrome/browser/ssl/https_only_mode_browsertest.cc +++ b/chrome/browser/ssl/https_only_mode_browsertest.cc
@@ -56,7 +56,8 @@ void SetUp() override { feature_list_.InitWithFeatures( /*enabled_features=*/{features::kHttpsOnlyMode}, - /*disabled_features=*/{features::kHttpsFirstModeV2}); + /*disabled_features=*/{features::kHttpsFirstModeV2, + features::kHttpsUpgrades}); InProcessBrowserTest::SetUp(); }
diff --git a/chrome/browser/ssl/https_only_mode_upgrade_interceptor.cc b/chrome/browser/ssl/https_only_mode_upgrade_interceptor.cc index 12be8068..b9db43a 100644 --- a/chrome/browser/ssl/https_only_mode_upgrade_interceptor.cc +++ b/chrome/browser/ssl/https_only_mode_upgrade_interceptor.cc
@@ -111,9 +111,8 @@ } // Check if the hostname is in the enterprise policy HTTP allowlist. - PrefService* prefs = profile->GetPrefs(); - if (IsHostnameInAllowlist(tentative_resource_request.url, - prefs->GetList(prefs::kHttpAllowlist))) { + if (IsHostnameInHttpAllowlist(tentative_resource_request.url, + profile->GetPrefs())) { std::move(callback).Run({}); return; }
diff --git a/chrome/browser/ssl/https_upgrades_browsertest.cc b/chrome/browser/ssl/https_upgrades_browsertest.cc index 4112035..0ca416f 100644 --- a/chrome/browser/ssl/https_upgrades_browsertest.cc +++ b/chrome/browser/ssl/https_upgrades_browsertest.cc
@@ -80,6 +80,10 @@ /*enabled_features=*/{features::kHttpsFirstModeV2, features::kHttpsUpgrades}, /*disabled_features=*/{}); + } else if (GetParam() == HttpsUpgradesTestType::kNeither) { + feature_list_.InitWithFeatures( + /*enabled_features=*/{features::kHttpsFirstModeV2}, + /*disabled_features=*/{features::kHttpsUpgrades}); } else { feature_list_.InitAndEnableFeature(features::kHttpsFirstModeV2); }
diff --git a/chrome/browser/ssl/https_upgrades_interceptor.cc b/chrome/browser/ssl/https_upgrades_interceptor.cc index 3c3234d..685d3b40 100644 --- a/chrome/browser/ssl/https_upgrades_interceptor.cc +++ b/chrome/browser/ssl/https_upgrades_interceptor.cc
@@ -314,8 +314,8 @@ // Don't upgrade navigation if it is allowlisted. // First, check the enterprise policy HTTP allowlist. PrefService* prefs = profile->GetPrefs(); - if (IsHostnameInAllowlist(tentative_resource_request.url, - prefs->GetList(prefs::kHttpAllowlist))) { + if (IsHostnameInHttpAllowlist(tentative_resource_request.url, + profile->GetPrefs())) { RecordNavigationRequestSecurityLevel( NavigationRequestSecurityLevel::kAllowlisted); std::move(callback).Run({});
diff --git a/chrome/browser/ssl/https_upgrades_util.cc b/chrome/browser/ssl/https_upgrades_util.cc index b20c4eb9..dbd2d21a 100644 --- a/chrome/browser/ssl/https_upgrades_util.cc +++ b/chrome/browser/ssl/https_upgrades_util.cc
@@ -5,11 +5,15 @@ #include "chrome/browser/ssl/https_upgrades_util.h" #include "base/values.h" +#include "chrome/common/pref_names.h" #include "components/content_settings/core/common/content_settings_pattern.h" +#include "components/prefs/pref_service.h" #include "url/gurl.h" -bool IsHostnameInAllowlist(const GURL& url, - const base::Value::List& allowed_hosts) { +bool IsHostnameInHttpAllowlist(const GURL& url, PrefService* prefs) { + const base::Value::List& allowed_hosts = + prefs->GetList(prefs::kHttpAllowlist); + // Though this is not technically a Content Setting, ContentSettingsPattern // aligns better than URLMatcher with the rules from // https://chromeenterprise.google/policies/url-patterns/. @@ -28,3 +32,31 @@ } return false; } + +void AllowHttpForHostnamesForTesting(const std::vector<std::string>& hostnames, + PrefService* prefs) { + DCHECK(prefs->GetList(prefs::kHttpAllowlist).empty()); + + base::Value::List allowed_hosts; + for (const std::string& hostname : hostnames) { + allowed_hosts.Append(hostname); + } + prefs->SetList(prefs::kHttpAllowlist, std::move(allowed_hosts)); +} + +void ClearHttpAllowlistForHostnamesForTesting(PrefService* prefs) { + base::Value::List empty_list; + prefs->SetList(prefs::kHttpAllowlist, std::move(empty_list)); +} + +ScopedAllowHttpForHostnamesForTesting::ScopedAllowHttpForHostnamesForTesting( + const std::vector<std::string>& hostnames, + PrefService* prefs) + : prefs_(prefs) { + AllowHttpForHostnamesForTesting(hostnames, prefs); +} + +ScopedAllowHttpForHostnamesForTesting:: + ~ScopedAllowHttpForHostnamesForTesting() { + ClearHttpAllowlistForHostnamesForTesting(prefs_); +}
diff --git a/chrome/browser/ssl/https_upgrades_util.h b/chrome/browser/ssl/https_upgrades_util.h index 06db64c..2798513 100644 --- a/chrome/browser/ssl/https_upgrades_util.h +++ b/chrome/browser/ssl/https_upgrades_util.h
@@ -5,9 +5,12 @@ #ifndef CHROME_BROWSER_SSL_HTTPS_UPGRADES_UTIL_H_ #define CHROME_BROWSER_SSL_HTTPS_UPGRADES_UTIL_H_ +#include "base/memory/stack_allocated.h" #include "base/values.h" #include "url/gurl.h" +class PrefService; + // Helper for applying the HttpAllowlist enterprise policy. Checks if the // hostname of `url` matches any of the hostnames or hostname patterns in the // `allowed_hosts` list. Does not allow blanket host wildcards (i.e., "*" which @@ -15,7 +18,34 @@ // "[*.]example.com"). Entries in `allowed_hosts` should follow the rules in // https://chromeenterprise.google/policies/url-patterns/ (or they'll be // ignored). -bool IsHostnameInAllowlist(const GURL& url, - const base::Value::List& allowed_hosts); +bool IsHostnameInHttpAllowlist(const GURL& url, PrefService* prefs); + +// Adds `hostnames` to the HttpAllowlist enterprise policy for testing. +// Expects the allowlist to be empty before updating it. +void AllowHttpForHostnamesForTesting(const std::vector<std::string>& hostnames, + PrefService* prefs); + +// Clears HttpAllowlist enterprise policy for testing. +void ClearHttpAllowlistForHostnamesForTesting(PrefService* prefs); + +// An instance of this class adds `hostnames` to the HttpAllowlist enterprise +// policy for testing and clears the allowlist when it goes out of scope. +class ScopedAllowHttpForHostnamesForTesting { + STACK_ALLOCATED(); + + public: + explicit ScopedAllowHttpForHostnamesForTesting( + const std::vector<std::string>& hostnames, + PrefService* prefs); + ScopedAllowHttpForHostnamesForTesting( + const ScopedAllowHttpForHostnamesForTesting&) = delete; + ScopedAllowHttpForHostnamesForTesting& operator=( + const ScopedAllowHttpForHostnamesForTesting&) = delete; + + ~ScopedAllowHttpForHostnamesForTesting(); + + private: + PrefService* prefs_; +}; #endif // CHROME_BROWSER_SSL_HTTPS_UPGRADES_UTIL_H_
diff --git a/chrome/browser/ssl/security_state_tab_helper_browsertest.cc b/chrome/browser/ssl/security_state_tab_helper_browsertest.cc index 3db0dd3..425aca3 100644 --- a/chrome/browser/ssl/security_state_tab_helper_browsertest.cc +++ b/chrome/browser/ssl/security_state_tab_helper_browsertest.cc
@@ -31,6 +31,7 @@ #include "chrome/browser/profiles/profile_window.h" #include "chrome/browser/safe_browsing/chrome_password_protection_service.h" #include "chrome/browser/ssl/cert_verifier_browser_test.h" +#include "chrome/browser/ssl/https_upgrades_util.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_commands.h" #include "chrome/browser/ui/browser_finder.h" @@ -1363,6 +1364,10 @@ SecurityStateTabHelper::FromWebContents(contents); ASSERT_TRUE(helper); + // Disable HTTPS upgrades on nonexistent.test for this test to work. + ScopedAllowHttpForHostnamesForTesting scoped_allow_http( + {"nonexistent.test"}, browser()->profile()->GetPrefs()); + // Navigate to a URL that results in an error page. Even though the displayed // URL is http://, there shouldn't be a Not Secure warning because the browser // hasn't really navigated to an http:// page.
diff --git a/chrome/browser/ssl/ssl_browsertest.cc b/chrome/browser/ssl/ssl_browsertest.cc index 81a37efe..e4199ae 100644 --- a/chrome/browser/ssl/ssl_browsertest.cc +++ b/chrome/browser/ssl/ssl_browsertest.cc
@@ -55,6 +55,7 @@ #include "chrome/browser/ssl/cert_verifier_browser_test.h" #include "chrome/browser/ssl/certificate_reporting_test_utils.h" #include "chrome/browser/ssl/chrome_security_blocking_page_factory.h" +#include "chrome/browser/ssl/https_upgrades_util.h" #include "chrome/browser/ssl/security_state_tab_helper.h" #include "chrome/browser/ssl/ssl_browsertest_util.h" #include "chrome/browser/ssl/ssl_error_controller_client.h" @@ -109,6 +110,7 @@ #include "components/security_interstitials/content/ssl_error_handler.h" #include "components/security_interstitials/content/stateful_ssl_host_state_delegate.h" #include "components/security_interstitials/core/controller_client.h" +#include "components/security_interstitials/core/https_only_mode_metrics.h" #include "components/security_interstitials/core/metrics_helper.h" #include "components/security_interstitials/core/pref_names.h" #include "components/security_state/core/security_state.h" @@ -6451,16 +6453,24 @@ ASSERT_TRUE(embedded_test_server()->Start()); ASSERT_TRUE(https_server_.Start()); + // This test posts to does-not-exist.test. Disable HTTPS upgrades on this + // hostname for the test to work. + // TODO(crbug.com/1394910): Remove the allowlist entry. + ScopedAllowHttpForHostnamesForTesting scoped_allow_http( + {"does-not-exist.test"}, browser()->profile()->GetPrefs()); + std::string replacement_path = GetFilePathWithHostAndPortReplacement( "/ssl/page_displays_form_redirects_301_insecure.html", embedded_test_server()->host_port_pair()); ASSERT_TRUE(ui_test_utils::NavigateToURL( browser(), https_server_.GetURL(replacement_path))); + WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents(); content::TestNavigationObserver nav_observer(tab, 1); ASSERT_TRUE(content::ExecuteScript(tab, "submitForm();")); nav_observer.Wait(); + security_interstitials::SecurityInterstitialTabHelper* helper = security_interstitials::SecurityInterstitialTabHelper::FromWebContents( tab); @@ -6487,6 +6497,12 @@ ASSERT_TRUE(embedded_test_server()->Start()); ASSERT_TRUE(https_server_.Start()); + // This test posts to does-not-exist.test. Disable HTTPS upgrades on this + // hostname for the test to work. + // TODO(crbug.com/1394910): Remove the allowlist entry. + ScopedAllowHttpForHostnamesForTesting scoped_allow_http( + {"does-not-exist.test"}, browser()->profile()->GetPrefs()); + std::string replacement_path = GetFilePathWithHostAndPortReplacement( "/ssl/page_displays_form_redirects_302_insecure.html", embedded_test_server()->host_port_pair()); @@ -6551,6 +6567,12 @@ base::BindRepeating(&FormActionHTTPRedirectHandler, &https_server_)); ASSERT_TRUE(https_server_.Start()); + // This test redirects to example.org. Disable HTTPS upgrades on this + // hostname for the test to work. + // TODO(crbug.com/1394910): Remove the allowlist entry. + ScopedAllowHttpForHostnamesForTesting scoped_allow_http( + {"example.org"}, browser()->profile()->GetPrefs()); + std::string replacement_path = GetFilePathWithHostAndPortReplacement( "/ssl/page_displays_form_redirects_insecure_get.html", embedded_test_server()->host_port_pair());
diff --git a/chrome/browser/ui/ash/wallpaper_controller_client_impl.cc b/chrome/browser/ui/ash/wallpaper_controller_client_impl.cc index 3d605f5..70fa500 100644 --- a/chrome/browser/ui/ash/wallpaper_controller_client_impl.cc +++ b/chrome/browser/ui/ash/wallpaper_controller_client_impl.cc
@@ -574,7 +574,7 @@ Profile* profile = ProfileHelper::Get()->GetProfileByAccountId(account_id); google_photos_photos_fetchers_.insert( {account_id, - std::make_unique<wallpaper_handlers::GooglePhotosPhotosFetcher>( + wallpaper_fetcher_delegate_->CreateGooglePhotosPhotosFetcher( profile)}); } auto fetched_callback = @@ -595,7 +595,7 @@ Profile* profile = ProfileHelper::Get()->GetProfileByAccountId(account_id); google_photos_photos_fetchers_.insert( {account_id, - std::make_unique<wallpaper_handlers::GooglePhotosPhotosFetcher>( + wallpaper_fetcher_delegate_->CreateGooglePhotosPhotosFetcher( profile)}); } auto fetched_callback = base::BindOnce(
diff --git a/chrome/browser/ui/bluetooth/chrome_bluetooth_chooser_controller.cc b/chrome/browser/ui/bluetooth/chrome_bluetooth_chooser_controller.cc index 2a61470..757e05f 100644 --- a/chrome/browser/ui/bluetooth/chrome_bluetooth_chooser_controller.cc +++ b/chrome/browser/ui/bluetooth/chrome_bluetooth_chooser_controller.cc
@@ -53,10 +53,7 @@ : permissions::BluetoothChooserController( owner, event_handler, - CreateExtensionAwareChooserTitle( - owner, - IDS_BLUETOOTH_DEVICE_CHOOSER_PROMPT_ORIGIN, - IDS_BLUETOOTH_DEVICE_CHOOSER_PROMPT_EXTENSION_NAME)) {} + CreateChooserTitle(owner, IDS_BLUETOOTH_DEVICE_CHOOSER_PROMPT)) {} ChromeBluetoothChooserController::~ChromeBluetoothChooserController() = default;
diff --git a/chrome/browser/ui/color/material_omnibox_color_mixer.cc b/chrome/browser/ui/color/material_omnibox_color_mixer.cc index f1b9e25..9235f70 100644 --- a/chrome/browser/ui/color/material_omnibox_color_mixer.cc +++ b/chrome/browser/ui/color/material_omnibox_color_mixer.cc
@@ -32,5 +32,4 @@ mixer[kColorToolbarBackgroundSubtleEmphasisHovered] = ui::GetResultingPaintColor(ui::kColorSysStateHoverBrightBlendProtection, kColorToolbarBackgroundSubtleEmphasis); - mixer[kColorOmniboxText] = {ui::kColorSysOnSurface}; }
diff --git a/chrome/browser/ui/color/omnibox_color_mixer.cc b/chrome/browser/ui/color/omnibox_color_mixer.cc index 8b23acf..e9adddc 100644 --- a/chrome/browser/ui/color/omnibox_color_mixer.cc +++ b/chrome/browser/ui/color/omnibox_color_mixer.cc
@@ -4,9 +4,13 @@ #include "chrome/browser/ui/color/omnibox_color_mixer.h" +#include "base/feature_list.h" +#include "base/strings/string_number_conversions.h" #include "chrome/browser/ui/color/chrome_color_id.h" #include "chrome/browser/ui/color/chrome_color_provider_utils.h" +#include "components/omnibox/common/omnibox_features.h" #include "third_party/skia/include/core/SkColor.h" +#include "ui/base/ui_base_features.h" #include "ui/color/color_id.h" #include "ui/color/color_mixer.h" #include "ui/color/color_provider.h" @@ -20,6 +24,75 @@ // The contrast for omnibox colors in high contrast mode. constexpr float kOmniboxHighContrastRatio = 6.0f; +// Apply updates to the Omnibox text color tokens per GM3 spec. +void ApplyGM3OmniboxTextColor(ui::ColorMixer& mixer, + const ui::ColorProviderManager::Key& key) { + const bool gm3_text_color_enabled = + features::IsChromeRefresh2023() || + base::FeatureList::IsEnabled(omnibox::kOmniboxSteadyStateTextColor); + + // Apply omnibox text color updates only to non-themed clients. + if (!gm3_text_color_enabled || key.custom_theme) { + return; + } + + // Retrieve GM3 omnibox text color params (Dark Mode). + const std::string dark_text_color_param = + omnibox::kOmniboxTextColorDarkMode.Get(); + const std::string dark_text_color_dimmed_param = + omnibox::kOmniboxTextColorDimmedDarkMode.Get(); + + // Retrieve GM3 omnibox text color params (Light Mode). + const std::string light_text_color_param = + omnibox::kOmniboxTextColorLightMode.Get(); + const std::string light_text_color_dimmed_param = + omnibox::kOmniboxTextColorDimmedLightMode.Get(); + + const auto string_to_skcolor = [](const std::string& rgb_str, + SkColor* result) { + // Valid color strings are of the form 0xRRGGBB or 0xAARRGGBB. + const bool valid = result && (rgb_str.size() == 8 || rgb_str.size() == 10); + if (!valid) { + return false; + } + + uint32_t parsed = 0; + const bool success = base::HexStringToUInt(rgb_str, &parsed); + if (success) { + *result = SkColorSetA(static_cast<SkColor>(parsed), SK_AlphaOPAQUE); + } + return success; + }; + + SkColor dark_text_color = 0; + SkColor dark_text_color_dimmed = 0; + + SkColor light_text_color = 0; + SkColor light_text_color_dimmed = 0; + + const bool success = + string_to_skcolor(dark_text_color_param, &dark_text_color) && + string_to_skcolor(dark_text_color_dimmed_param, + &dark_text_color_dimmed) && + string_to_skcolor(light_text_color_param, &light_text_color) && + string_to_skcolor(light_text_color_dimmed_param, + &light_text_color_dimmed); + + if (!success) { + return; + } + + const auto selected_text_color = ui::SelectBasedOnDarkInput( + kColorToolbar, dark_text_color, light_text_color); + + mixer[kColorOmniboxText] = {selected_text_color}; + + const auto selected_text_color_dimmed = ui::SelectBasedOnDarkInput( + kColorToolbar, dark_text_color_dimmed, light_text_color_dimmed); + + mixer[kColorOmniboxTextDimmed] = {selected_text_color_dimmed}; +} + } // namespace void AddOmniboxColorMixer(ui::ColorProvider* provider, @@ -217,4 +290,7 @@ kColorToolbar, SkColorSetRGB(0, 74, 119), SkColorSetRGB(211, 227, 253)); mixer[kColorOmniboxAnswerIconGM3Foreground] = ui::SelectBasedOnDarkInput( kColorToolbar, SkColorSetRGB(194, 231, 255), SkColorSetRGB(4, 30, 73)); + + // Override omnibox text color per GM3 spec. + ApplyGM3OmniboxTextColor(mixer, key); }
diff --git a/chrome/browser/ui/global_media_controls/cast_device_list_host.cc b/chrome/browser/ui/global_media_controls/cast_device_list_host.cc index fe2f3715..643bf2a 100644 --- a/chrome/browser/ui/global_media_controls/cast_device_list_host.cc +++ b/chrome/browser/ui/global_media_controls/cast_device_list_host.cc
@@ -134,9 +134,11 @@ } else if (sink.state == media_router::UIMediaSinkState::CONNECTED) { // We record stopping casting here even if we are starting casting, because // the existing session is being stopped and replaced by a new session. - // TODO(crbug.com/1411139): Call RecordStopCastingMetrics() here. + // TODO(crbug.com/1411139): Call RecordStopCastingMetrics() here instead. if (sink.provider == media_router::mojom::MediaRouteProviderId::DIAL) { DCHECK(sink.route); + MediaItemUIMetrics::RecordStopCastMode( + media_router::MediaCastMode::PRESENTATION); cast_controller_->StopCasting(sink.route->media_route_id()); } else { StartCasting(sink); @@ -172,7 +174,8 @@ if (cast_mode.value() == media_router::MediaCastMode::REMOTE_PLAYBACK) { media_remoting_callback_.Run(); } - // TODO(crbug.com/1411139): Call RecordStartCastingMetrics() here. + // TODO(crbug.com/1411139): Call RecordStartCastingMetrics() here instead. + MediaItemUIMetrics::RecordStartCastMode(cast_mode.value()); } void CastDeviceListHost::DestroyCastController() {
diff --git a/chrome/browser/ui/global_media_controls/media_item_ui_metrics.cc b/chrome/browser/ui/global_media_controls/media_item_ui_metrics.cc index be2357d..f95cb0cd1 100644 --- a/chrome/browser/ui/global_media_controls/media_item_ui_metrics.cc +++ b/chrome/browser/ui/global_media_controls/media_item_ui_metrics.cc
@@ -32,13 +32,23 @@ } // namespace +void MediaItemUIMetrics::RecordStartCastMode( + media_router::MediaCastMode cast_mode) { + base::UmaHistogramEnumeration(kStartCastingModeHistogramName, + GetGlobalMediaControlsCastMode(cast_mode)); +} +void MediaItemUIMetrics::RecordStopCastMode( + media_router::MediaCastMode cast_mode) { + base::UmaHistogramEnumeration(kStopCastingModeHistogramName, + GetGlobalMediaControlsCastMode(cast_mode)); +} + void MediaItemUIMetrics::RecordStartCastingMetrics( media_router::SinkIconType sink_icon_type, media_router::MediaCastMode cast_mode, GlobalMediaControlsEntryPoint entry_point) { MediaRouterMetrics::RecordMediaSinkTypeForGlobalMediaControls(sink_icon_type); - base::UmaHistogramEnumeration(kStartCastingModeHistogramName, - GetGlobalMediaControlsCastMode(cast_mode)); + RecordStartCastMode(cast_mode); GlobalMediaControlsCastActionAndEntryPoint action; switch (entry_point) { @@ -59,8 +69,7 @@ void MediaItemUIMetrics::RecordStopCastingMetrics( media_router::MediaCastMode cast_mode, GlobalMediaControlsEntryPoint entry_point) { - base::UmaHistogramEnumeration(kStopCastingModeHistogramName, - GetGlobalMediaControlsCastMode(cast_mode)); + RecordStopCastMode(cast_mode); GlobalMediaControlsCastActionAndEntryPoint action; switch (entry_point) {
diff --git a/chrome/browser/ui/global_media_controls/media_item_ui_metrics.h b/chrome/browser/ui/global_media_controls/media_item_ui_metrics.h index 126d0a3..4bd73ff 100644 --- a/chrome/browser/ui/global_media_controls/media_item_ui_metrics.h +++ b/chrome/browser/ui/global_media_controls/media_item_ui_metrics.h
@@ -33,6 +33,8 @@ class MediaItemUIMetrics { public: + static void RecordStartCastMode(media_router::MediaCastMode cast_mode); + static void RecordStopCastMode(media_router::MediaCastMode cast_mode); static void RecordStartCastingMetrics( media_router::SinkIconType sink_icon_type, media_router::MediaCastMode cast_mode,
diff --git a/chrome/browser/ui/hid/hid_chooser_controller.cc b/chrome/browser/ui/hid/hid_chooser_controller.cc index 21bbc09..bb0e570 100644 --- a/chrome/browser/ui/hid/hid_chooser_controller.cc +++ b/chrome/browser/ui/hid/hid_chooser_controller.cc
@@ -83,10 +83,8 @@ std::vector<blink::mojom::HidDeviceFilterPtr> filters, std::vector<blink::mojom::HidDeviceFilterPtr> exclusion_filters, content::HidChooser::Callback callback) - : ChooserController(CreateExtensionAwareChooserTitle( - render_frame_host, - IDS_HID_CHOOSER_PROMPT_ORIGIN, - IDS_HID_CHOOSER_PROMPT_EXTENSION_NAME)), + : ChooserController( + CreateChooserTitle(render_frame_host, IDS_HID_CHOOSER_PROMPT)), filters_(std::move(filters)), exclusion_filters_(std::move(exclusion_filters)), callback_(std::move(callback)),
diff --git a/chrome/browser/ui/layout_constants.cc b/chrome/browser/ui/layout_constants.cc index 68707652..e58fb77 100644 --- a/chrome/browser/ui/layout_constants.cc +++ b/chrome/browser/ui/layout_constants.cc
@@ -46,6 +46,12 @@ } case LOCATION_BAR_ICON_SIZE: return touch_ui ? 20 : 16; + case LOCATION_BAR_LEADING_ICON_SIZE: + return GetLayoutConstant(LOCATION_BAR_ICON_SIZE); + case LOCATION_BAR_TRAILING_ICON_SIZE: + return base::FeatureList::IsEnabled(features::kChromeRefresh2023) + ? 20 + : GetLayoutConstant(LOCATION_BAR_ICON_SIZE); case TAB_AFTER_TITLE_PADDING: return touch_ui ? 8 : 4; case TAB_ALERT_INDICATOR_CAPTURE_ICON_WIDTH:
diff --git a/chrome/browser/ui/layout_constants.h b/chrome/browser/ui/layout_constants.h index ac9506b..dc48748 100644 --- a/chrome/browser/ui/layout_constants.h +++ b/chrome/browser/ui/layout_constants.h
@@ -44,8 +44,18 @@ LOCATION_BAR_HEIGHT, // The size of the icons used inside the LocationBar. + // TODO(crbug.com/1399991): Deprecate this after the size of all location bar + // icons have moved to + // either `LOCATION_BAR_LEADING_ICON_SIZE` or + // `LOCATION_BAR_TRAILING_ICON_SIZE` LOCATION_BAR_ICON_SIZE, + // The size of the leading icons used inside the LocationBar. + LOCATION_BAR_LEADING_ICON_SIZE, + + // The size of the trailing icons used inside the LocationBar. + LOCATION_BAR_TRAILING_ICON_SIZE, + // The size of icons used in PageInfo bubbles. PAGE_INFO_ICON_SIZE,
diff --git a/chrome/browser/ui/serial/serial_chooser_controller.cc b/chrome/browser/ui/serial/serial_chooser_controller.cc index 2e9fa4c2..0401bc32 100644 --- a/chrome/browser/ui/serial/serial_chooser_controller.cc +++ b/chrome/browser/ui/serial/serial_chooser_controller.cc
@@ -27,10 +27,8 @@ content::RenderFrameHost* render_frame_host, std::vector<blink::mojom::SerialPortFilterPtr> filters, content::SerialChooser::Callback callback) - : ChooserController(CreateExtensionAwareChooserTitle( - render_frame_host, - IDS_SERIAL_PORT_CHOOSER_PROMPT_ORIGIN, - IDS_SERIAL_PORT_CHOOSER_PROMPT_EXTENSION_NAME)), + : ChooserController(CreateChooserTitle(render_frame_host, + IDS_SERIAL_PORT_CHOOSER_PROMPT)), filters_(std::move(filters)), callback_(std::move(callback)), initiator_document_(render_frame_host->GetWeakDocumentPtr()) {
diff --git a/chrome/browser/ui/toolbar/chrome_location_bar_model_delegate.cc b/chrome/browser/ui/toolbar/chrome_location_bar_model_delegate.cc index c63b469..ddf0644e 100644 --- a/chrome/browser/ui/toolbar/chrome_location_bar_model_delegate.cc +++ b/chrome/browser/ui/toolbar/chrome_location_bar_model_delegate.cc
@@ -34,6 +34,7 @@ #include "content/public/browser/web_contents.h" #include "content/public/common/url_constants.h" #include "extensions/common/constants.h" +#include "ui/base/ui_base_features.h" #if !BUILDFLAG(IS_ANDROID) #include "components/omnibox/browser/vector_icons.h" // nogncheck @@ -184,7 +185,9 @@ GetURL(&url); if (url.SchemeIs(content::kChromeUIScheme)) - return &omnibox::kProductIcon; + return (features::IsChromeRefresh2023()) + ? &omnibox::kProductChromeRefreshIcon + : &omnibox::kProductIcon; if (url.SchemeIs(extensions::kExtensionScheme)) return &omnibox::kExtensionAppIcon;
diff --git a/chrome/browser/ui/views/extensions/extensions_menu_item_view.cc b/chrome/browser/ui/views/extensions/extensions_menu_item_view.cc index 99c64538..f624a19 100644 --- a/chrome/browser/ui/views/extensions/extensions_menu_item_view.cc +++ b/chrome/browser/ui/views/extensions/extensions_menu_item_view.cc
@@ -216,8 +216,9 @@ const auto* const color_provider = GetColorProvider(); const SkColor icon_color = color_provider->GetColor(kColorExtensionMenuIcon); - if (pin_button_) + if (pin_button_) { views::InkDrop::Get(pin_button_)->SetBaseColor(icon_color); + } SetButtonIconWithColor( context_menu_button_, kBrowserToolsIcon, icon_color, @@ -241,26 +242,29 @@ } void ExtensionMenuItemView::UpdatePinButton() { - if (!pin_button_) + if (!pin_button_) { return; + } bool is_force_pinned = model_ && model_->IsActionForcePinned(controller_->GetId()); int pin_button_string_id = 0; - if (is_force_pinned) + if (is_force_pinned) { pin_button_string_id = IDS_EXTENSIONS_PINNED_BY_ADMIN; - else if (IsPinned()) + } else if (IsPinned()) { pin_button_string_id = IDS_EXTENSIONS_UNPIN_FROM_TOOLBAR; - else + } else { pin_button_string_id = IDS_EXTENSIONS_PIN_TO_TOOLBAR; + } pin_button_->SetTooltipText(l10n_util::GetStringUTF16(pin_button_string_id)); // Extension pinning is not available in Incognito as it leaves a trace of // user activity. pin_button_->SetEnabled(!is_force_pinned && !browser_->profile()->IsOffTheRecord()); - if (!GetWidget()) + if (!GetWidget()) { return; + } const auto* const color_provider = GetColorProvider(); const SkColor icon_color = color_provider->GetColor( IsPinned() ? kColorExtensionMenuPinButtonIcon : kColorExtensionMenuIcon);
diff --git a/chrome/browser/ui/views/extensions/extensions_menu_site_permissions_page_view.cc b/chrome/browser/ui/views/extensions/extensions_menu_site_permissions_page_view.cc index b4474bdd..302c2f9 100644 --- a/chrome/browser/ui/views/extensions/extensions_menu_site_permissions_page_view.cc +++ b/chrome/browser/ui/views/extensions/extensions_menu_site_permissions_page_view.cc
@@ -41,10 +41,7 @@ ExtensionsMenuSitePermissionsPageView::ExtensionsMenuSitePermissionsPageView( Browser* browser, - std::u16string extension_name, - ui::ImageModel extension_icon, extensions::ExtensionId extension_id, - bool is_show_requests_toggle_on, ExtensionsMenuNavigationHandler* navigation_handler) : browser_(browser), extension_id_(extension_id) { // TODO(crbug.com/1390952): Same stretch specification as @@ -88,10 +85,11 @@ .SetCrossAxisAlignment(views::LayoutAlignment::kStretch) .SetProperty(views::kFlexBehaviorKey, stretch_specification) - .AddChildren(views::Builder<views::ImageView>().SetImage( - extension_icon), - views::Builder<views::Label>().SetText( - extension_name)), + .AddChildren( + views::Builder<views::ImageView>().CopyAddressTo( + &extension_icon_), + views::Builder<views::Label>().CopyAddressTo( + &extension_name_)), // Close button. views::Builder<views::Button>( views::BubbleFrameView::CreateCloseButton( @@ -114,10 +112,6 @@ IDS_EXTENSIONS_MENU_SITE_PERMISSIONS_PAGE_SHOW_REQUESTS_LABEL)), views::Builder<views::ToggleButton>() .CopyAddressTo(&show_requests_toggle_) - .SetIsOn(is_show_requests_toggle_on) - .SetAccessibleName( - GetShowRequestsToggleAccessibleName( - is_show_requests_toggle_on)) .SetCallback(base::BindRepeating( &ExtensionsMenuSitePermissionsPageView:: OnShowRequestsTogglePressed, @@ -130,7 +124,7 @@ extensions::ExtensionId extension_id) { chrome::ShowExtensions(browser, extension_id); }, - browser, extension_id), + browser, extension_id_), /*icon_view=*/nullptr, l10n_util::GetStringUTF16( IDS_EXTENSIONS_MENU_SITE_PERMISSIONS_PAGE_SETTINGS_BUTTON), @@ -143,9 +137,21 @@ .BuildChildren(); } +void ExtensionsMenuSitePermissionsPageView::Update( + const std::u16string& extension_name, + const ui::ImageModel& extension_icon, + bool is_show_requests_toggle_on) { + extension_icon_->SetImage(extension_icon); + extension_name_->SetText(extension_name); + + UpdateShowRequestsToggle(is_show_requests_toggle_on); +} + void ExtensionsMenuSitePermissionsPageView::UpdateShowRequestsToggle( bool is_on) { show_requests_toggle_->SetIsOn(is_on); + show_requests_toggle_->SetAccessibleName( + GetShowRequestsToggleAccessibleName(is_on)); } void ExtensionsMenuSitePermissionsPageView::OnShowRequestsTogglePressed() {
diff --git a/chrome/browser/ui/views/extensions/extensions_menu_site_permissions_page_view.h b/chrome/browser/ui/views/extensions/extensions_menu_site_permissions_page_view.h index 09eee52..f341e89 100644 --- a/chrome/browser/ui/views/extensions/extensions_menu_site_permissions_page_view.h +++ b/chrome/browser/ui/views/extensions/extensions_menu_site_permissions_page_view.h
@@ -13,6 +13,8 @@ } // namespace ui namespace views { +class ImageView; +class Label; class ToggleButton; } // namespace views @@ -25,10 +27,7 @@ explicit ExtensionsMenuSitePermissionsPageView( Browser* browser, - std::u16string extension_name, - ui::ImageModel extension_icon, extensions::ExtensionId extension_id, - bool is_show_requests_toggle_on, ExtensionsMenuNavigationHandler* navigation_handler); ExtensionsMenuSitePermissionsPageView( const ExtensionsMenuSitePermissionsPageView&) = delete; @@ -36,6 +35,11 @@ const ExtensionsMenuSitePermissionsPageView&) = delete; ~ExtensionsMenuSitePermissionsPageView() override = default; + // Updates the page contents with the given parameters. + void Update(const std::u16string& extension_name, + const ui::ImageModel& extension_icon, + bool is_show_requests_toggle_on); + // Updates `show_requests_toggle_` state to `is_on`. void UpdateShowRequestsToggle(bool is_on); @@ -54,6 +58,8 @@ const raw_ptr<Browser> browser_; extensions::ExtensionId extension_id_; + raw_ptr<views::ImageView> extension_icon_; + raw_ptr<views::Label> extension_name_; raw_ptr<views::ToggleButton> show_requests_toggle_; };
diff --git a/chrome/browser/ui/views/extensions/extensions_menu_site_permissions_page_view_unittest.cc b/chrome/browser/ui/views/extensions/extensions_menu_site_permissions_page_view_unittest.cc index 21562d6a..b17d763 100644 --- a/chrome/browser/ui/views/extensions/extensions_menu_site_permissions_page_view_unittest.cc +++ b/chrome/browser/ui/views/extensions/extensions_menu_site_permissions_page_view_unittest.cc
@@ -271,3 +271,66 @@ site_permissions_page()->GetShowRequestsToggleForTesting()->GetIsOn()); EXPECT_THAT(GetExtensionsShowingRequests(), testing::IsEmpty()); } + +// Test that navigating to a new site where the user doesn't have runtime host +// permissions controls (e.g restricted site) closes the site permissions page. +TEST_F(ExtensionsSitePermissionsPageViewUnitTest, + PageNavigationWithMenuOpen_UserLosesRuntimeHostPermissionsControls) { + content::WebContentsTester* web_contents_tester = + AddWebContentsAndGetTester(); + + auto extension = + InstallExtensionWithHostPermissions("Extension", {"<all_urls>"}); + + const GURL url("http://www.non-restricted.com"); + web_contents_tester->NavigateAndCommit(url); + WaitForAnimation(); + + ShowSitePermissionsPage(extension->id()); + EXPECT_FALSE(IsMainPageOpened()); + EXPECT_TRUE(IsSitePermissionsPageOpened(extension->id())); + + // While the menu is open, navigate to an url where extension should not have + // a site permissions page. + const GURL restricted_url("chrome://extensions"); + web_contents_tester->NavigateAndCommit(restricted_url); + WaitForAnimation(); + + // Menu should navigate back to main page since site permissions page should + // not be visible for the new url. + EXPECT_TRUE(IsMainPageOpened()); + EXPECT_FALSE(IsSitePermissionsPageOpened(extension->id())); +} + +// Test that navigating to a new site where the user still has runtime host +// permissions controls updates the page contents. +TEST_F(ExtensionsSitePermissionsPageViewUnitTest, + PageNavigationWithMenuOpen_UserMaintainsRuntimeHostPermissionsControls) { + content::WebContentsTester* web_contents_tester = + AddWebContentsAndGetTester(); + + auto extension = + InstallExtensionWithHostPermissions("Extension", {"<all_urls>"}); + + const GURL url_a("http://www.a.com"); + web_contents_tester->NavigateAndCommit(url_a); + WaitForAnimation(); + + ShowSitePermissionsPage(extension->id()); + EXPECT_FALSE(IsMainPageOpened()); + EXPECT_TRUE(IsSitePermissionsPageOpened(extension->id())); + + // While the menu is open, navigate to an url where extension also should have + // a site permissions page. + const GURL url_b("http://www.b.com"); + web_contents_tester->NavigateAndCommit(url_b); + WaitForAnimation(); + + // Menu should stay open in site permissions page for `extension`. + EXPECT_FALSE(IsMainPageOpened()); + EXPECT_TRUE(IsSitePermissionsPageOpened(extension->id())); +} + +// TODO(crbug.com/1390952): Verify page content changes when extension is +// updated. This will be easier to do once we have the site access radio +// buttons, as we can change to the correct site access.
diff --git a/chrome/browser/ui/views/extensions/extensions_menu_view_controller.cc b/chrome/browser/ui/views/extensions/extensions_menu_view_controller.cc index 848a9f6..ae9a78f 100644 --- a/chrome/browser/ui/views/extensions/extensions_menu_view_controller.cc +++ b/chrome/browser/ui/views/extensions/extensions_menu_view_controller.cc
@@ -242,6 +242,10 @@ *GetExtension(browser_, extension_id), *browser_->profile(), *toolbar_model_, *GetActiveWebContents())); + auto site_permissions_page = + std::make_unique<ExtensionsMenuSitePermissionsPageView>( + browser_, extension_id, this); + const int icon_size = ChromeLayoutProvider::Get()->GetDistanceMetric( DISTANCE_EXTENSIONS_MENU_EXTENSION_ICON_SIZE); std::unique_ptr<ExtensionActionViewController> action_controller = @@ -254,11 +258,9 @@ bool is_show_requests_toggle_on = extensions::SitePermissionsHelper(browser_->profile()) .ShowAccessRequestsInToolbar(extension_id); + site_permissions_page->Update(extension_name, extension_icon, + is_show_requests_toggle_on); - auto site_permissions_page = - std::make_unique<ExtensionsMenuSitePermissionsPageView>( - browser_, extension_name, extension_icon, extension_id, - is_show_requests_toggle_on, this); SwitchToPage(std::move(site_permissions_page)); } @@ -289,29 +291,64 @@ content::WebContents* web_contents) { DCHECK(current_page_); - ExtensionsMenuMainPageView* main_page = GetMainPage(current_page_); - if (main_page && web_contents) { - std::u16string current_site = GetCurrentHost(web_contents); - bool is_site_settings_toggle_visible = - IsSiteSettingsToggleVisible(*toolbar_model_, web_contents); - bool is_site_settings_toggle_on = - IsSiteSettingsToggleOn(browser_, web_contents); - main_page->Update(current_site, is_site_settings_toggle_visible, - is_site_settings_toggle_on); + if (!web_contents) { + return; + } - std::vector<ExtensionMenuItemView*> menu_items = main_page->GetMenuItems(); - for (auto* menu_item : menu_items) { - const extensions::Extension* extension = - extensions::ExtensionRegistry::Get(browser_->profile()) - ->enabled_extensions() - .GetByID(menu_item->view_controller()->GetId()); - CHECK(extension); + auto* site_permissions_page = GetSitePermissionsPage(current_page_); + if (site_permissions_page) { + extensions::ExtensionId extension_id = + site_permissions_page->extension_id(); - ExtensionMenuItemView::SitePermissionsButtonState - site_permissions_button_state = GetSitePermissionsButtonState( - *extension, *browser_->profile(), *toolbar_model_, *web_contents); - menu_item->Update(site_permissions_button_state); + // Navigate back to the main page if extension should not have a site + // permissions page. + if (!CanUserCustomizeExtensionSiteAccess( + *GetExtension(browser_, extension_id), *browser_->profile(), + *toolbar_model_, *web_contents)) { + OpenMainPage(); + return; } + + // Otherwise, update the page content. + const int icon_size = ChromeLayoutProvider::Get()->GetDistanceMetric( + DISTANCE_EXTENSIONS_MENU_EXTENSION_ICON_SIZE); + std::unique_ptr<ExtensionActionViewController> action_controller = + ExtensionActionViewController::Create(extension_id, browser_, + extensions_container_); + + std::u16string extension_name = action_controller->GetActionName(); + ui::ImageModel extension_icon = action_controller->GetIcon( + GetActiveWebContents(), gfx::Size(icon_size, icon_size)); + bool is_show_requests_toggle_on = + extensions::SitePermissionsHelper(browser_->profile()) + .ShowAccessRequestsInToolbar(extension_id); + + site_permissions_page->Update(extension_name, extension_icon, + is_show_requests_toggle_on); + return; + } + + ExtensionsMenuMainPageView* main_page = GetMainPage(current_page_); + DCHECK(main_page); + + std::u16string current_site = GetCurrentHost(web_contents); + bool is_site_settings_toggle_visible = + IsSiteSettingsToggleVisible(*toolbar_model_, web_contents); + bool is_site_settings_toggle_on = + IsSiteSettingsToggleOn(browser_, web_contents); + main_page->Update(current_site, is_site_settings_toggle_visible, + is_site_settings_toggle_on); + + std::vector<ExtensionMenuItemView*> menu_items = main_page->GetMenuItems(); + for (auto* menu_item : menu_items) { + const extensions::Extension* extension = + GetExtension(browser_, menu_item->view_controller()->GetId()); + CHECK(extension); + + ExtensionMenuItemView::SitePermissionsButtonState + site_permissions_button_state = GetSitePermissionsButtonState( + *extension, *browser_->profile(), *toolbar_model_, *web_contents); + menu_item->Update(site_permissions_button_state); } }
diff --git a/chrome/browser/ui/views/location_bar/star_view.cc b/chrome/browser/ui/views/location_bar/star_view.cc index 4b1f8b4..270f14c4 100644 --- a/chrome/browser/ui/views/location_bar/star_view.cc +++ b/chrome/browser/ui/views/location_bar/star_view.cc
@@ -36,6 +36,7 @@ #include "content/public/browser/web_contents.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/metadata/metadata_impl_macros.h" +#include "ui/base/ui_base_features.h" #include "ui/gfx/color_utils.h" #include "ui/gfx/paint_vector_icon.h" #include "ui/views/animation/ink_drop.h" @@ -111,6 +112,11 @@ } const gfx::VectorIcon& StarView::GetVectorIcon() const { + if (features::IsChromeRefresh2023()) { + return GetActive() ? omnibox::kStarActiveChromeRefreshIcon + : omnibox::kStarChromeRefreshIcon; + } + return GetActive() ? omnibox::kStarActiveIcon : omnibox::kStarIcon; }
diff --git a/chrome/browser/ui/views/omnibox/omnibox_view_views.cc b/chrome/browser/ui/views/omnibox/omnibox_view_views.cc index f195885..bb423ef4 100644 --- a/chrome/browser/ui/views/omnibox/omnibox_view_views.cc +++ b/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
@@ -90,6 +90,7 @@ #include "ui/base/metadata/metadata_impl_macros.h" #include "ui/base/models/image_model.h" #include "ui/base/models/simple_menu_model.h" +#include "ui/base/ui_base_features.h" #include "ui/compositor/layer.h" #include "ui/events/event.h" #include "ui/gfx/canvas.h" @@ -617,8 +618,12 @@ void OmniboxViewViews::OnThemeChanged() { views::Textfield::OnThemeChanged(); - set_placeholder_text_color( - GetColorProvider()->GetColor(kColorOmniboxTextDimmed)); + bool gm3_text_color_enabled = + features::IsChromeRefresh2023() || + base::FeatureList::IsEnabled(omnibox::kOmniboxSteadyStateTextColor); + + set_placeholder_text_color(GetColorProvider()->GetColor( + gm3_text_color_enabled ? kColorOmniboxText : kColorOmniboxTextDimmed)); EmphasizeURLComponents(); }
diff --git a/chrome/browser/ui/views/page_action/page_action_icon_view.cc b/chrome/browser/ui/views/page_action/page_action_icon_view.cc index 9abb277..11a3c5f3 100644 --- a/chrome/browser/ui/views/page_action/page_action_icon_view.cc +++ b/chrome/browser/ui/views/page_action/page_action_icon_view.cc
@@ -37,7 +37,7 @@ } int PageActionIconView::Delegate::GetPageActionIconSize() const { - return GetLayoutConstant(LOCATION_BAR_ICON_SIZE); + return GetLayoutConstant(LOCATION_BAR_TRAILING_ICON_SIZE); } gfx::Insets PageActionIconView::Delegate::GetPageActionIconInsets(
diff --git a/chrome/browser/ui/views/page_action/zoom_view.cc b/chrome/browser/ui/views/page_action/zoom_view.cc index db33117..8624401 100644 --- a/chrome/browser/ui/views/page_action/zoom_view.cc +++ b/chrome/browser/ui/views/page_action/zoom_view.cc
@@ -14,6 +14,7 @@ #include "ui/accessibility/ax_node_data.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/metadata/metadata_impl_macros.h" +#include "ui/base/ui_base_features.h" #include "ui/events/event.h" #include "ui/gfx/geometry/size.h" @@ -73,10 +74,20 @@ current_zoom_percent_ = zoom_controller->GetZoomPercent(); // The icon is hidden when the zoom level is default. - icon_ = zoom_controller && zoom_controller->GetZoomRelativeToDefault() == - zoom::ZoomController::ZOOM_BELOW_DEFAULT_ZOOM - ? &kZoomMinusIcon - : &kZoomPlusIcon; + + if (features::IsChromeRefresh2023()) { + icon_ = + zoom_controller && zoom_controller->GetZoomRelativeToDefault() == + zoom::ZoomController::ZOOM_BELOW_DEFAULT_ZOOM + ? &kZoomMinusChromeRefreshIcon + : &kZoomPlusChromeRefreshIcon; + } else { + icon_ = + zoom_controller && zoom_controller->GetZoomRelativeToDefault() == + zoom::ZoomController::ZOOM_BELOW_DEFAULT_ZOOM + ? &kZoomMinusIcon + : &kZoomPlusIcon; + } UpdateIconImage(); // Visibility must be enabled before the bubble is shown to ensure the
diff --git a/chrome/browser/ui/views/side_panel/extensions/extension_side_panel_browsertest.cc b/chrome/browser/ui/views/side_panel/extensions/extension_side_panel_browsertest.cc index 22b966d..1c00dab 100644 --- a/chrome/browser/ui/views/side_panel/extensions/extension_side_panel_browsertest.cc +++ b/chrome/browser/ui/views/side_panel/extensions/extension_side_panel_browsertest.cc
@@ -6,6 +6,7 @@ #include "base/test/scoped_feature_list.h" #include "chrome/browser/extensions/api/side_panel/side_panel_api.h" #include "chrome/browser/extensions/extension_browsertest.h" +#include "chrome/browser/extensions/extension_tab_util.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/views/frame/browser_view.h" @@ -16,6 +17,8 @@ #include "chrome/browser/ui/views/side_panel/side_panel_entry_observer.h" #include "chrome/browser/ui/views/side_panel/side_panel_registry.h" #include "chrome/browser/ui/views/side_panel/side_panel_registry_observer.h" +#include "chrome/test/base/ui_test_utils.h" +#include "components/sessions/content/session_tab_helper.h" #include "content/public/test/browser_test.h" #include "content/public/test/browser_test_utils.h" #include "content/public/test/test_utils.h" @@ -117,21 +120,83 @@ } protected: + int GetCurrentTabId() { + return ExtensionTabUtil::GetTabId( + browser()->tab_strip_model()->GetActiveWebContents()); + } + // Calls chrome.sidePanel.setOptions() for the given `extension`, `path` and // `enabled` and returns when the API call is complete. void RunSetOptions(const Extension& extension, - const std::string& path, + absl::optional<int> tab_id, + absl::optional<std::string> path, bool enabled) { auto function = base::MakeRefCounted<SidePanelSetOptionsFunction>(); function->set_extension(&extension); + std::string tab_id_arg = + tab_id.has_value() ? base::StringPrintf(R"("tabId":%d,)", *tab_id) : ""; + std::string path_arg = + path.has_value() ? base::StringPrintf(R"("path":"%s",)", path->c_str()) + : ""; std::string args = - base::StringPrintf(R"([{"path":"%s","enabled":%s}])", path.c_str(), - enabled ? "true" : "false"); + base::StringPrintf(R"([{%s%s"enabled":%s}])", tab_id_arg.c_str(), + path_arg.c_str(), enabled ? "true" : "false"); EXPECT_TRUE(api_test_utils::RunFunction(function.get(), args, profile())) << function->GetError(); } + // Disables the extension's side panel for the current tab. + void DisableForCurrentTab(const Extension& extension) { + ExtensionSidePanelRegistryWaiter waiter(global_registry(), extension.id()); + RunSetOptions(extension, GetCurrentTabId(), /*path=*/absl::nullopt, + /*enabled=*/false); + waiter.WaitForDeregistration(); + EXPECT_FALSE(global_registry()->GetEntryForKey(GetKey(extension.id()))); + EXPECT_FALSE(side_panel_coordinator()->IsSidePanelShowing()); + } + + // Shows a side panel entry and waits for the entry to be shown. + void ShowEntryAndWait(const SidePanelEntry::Key& key) { + TestSidePanelEntryWaiter extension_entry_waiter( + global_registry()->GetEntryForKey(key)); + side_panel_coordinator()->Show(key); + extension_entry_waiter.WaitForEntryShown(); + EXPECT_TRUE(side_panel_coordinator()->IsSidePanelShowing()); + } + + // Runs a script in the extension's side panel WebContents to retrieve the + // value of document.sidePanelTemp. + std::string GetGlobalVariableInExtensionSidePanel( + const ExtensionId& extension_id) { + auto* extension_coordinator = + extensions::ExtensionSidePanelManager::GetOrCreateForBrowser(browser()) + ->GetExtensionCoordinatorForTesting(extension_id); + + std::string result; + static constexpr char kScript[] = + "domAutomationController.send(document.sidePanelTemp);"; + + EXPECT_TRUE(content::ExecuteScriptAndExtractString( + extension_coordinator->GetHostWebContentsForTesting(), kScript, + &result)); + return result; + } + + // Runs a script in the extension's side panel WebContents to set the value of + // document.sidePanelTemp to `value`. + void SetGlobalVariableInExtensionSidePanel(const ExtensionId& extension_id, + const std::string& value) { + auto* extension_coordinator = + extensions::ExtensionSidePanelManager::GetOrCreateForBrowser(browser()) + ->GetExtensionCoordinatorForTesting(extension_id); + + std::string script = + base::StringPrintf(R"(document.sidePanelTemp = "%s";)", value.c_str()); + ASSERT_TRUE(content::ExecuteScript( + extension_coordinator->GetHostWebContentsForTesting(), script.c_str())); + } + SidePanelRegistry* global_registry() { return SidePanelCoordinator::GetGlobalSidePanelRegistry(browser()); } @@ -182,8 +247,7 @@ test_data_dir_.AppendASCII("api_test/side_panel/simple_default")); ASSERT_TRUE(extension); - SidePanelEntry::Key extension_key = - SidePanelEntry::Key(SidePanelEntry::Id::kExtension, extension->id()); + SidePanelEntry::Key extension_key = GetKey(extension->id()); SidePanelEntry* extension_entry = global_registry()->GetEntryForKey(extension_key); ASSERT_TRUE(extension_entry); @@ -224,8 +288,7 @@ scoped_refptr<const extensions::Extension> extension = LoadExtension( test_data_dir_.AppendASCII("api_test/side_panel/simple_default")); ASSERT_TRUE(extension); - SidePanelEntry::Key extension_key = - SidePanelEntry::Key(SidePanelEntry::Id::kExtension, extension->id()); + SidePanelEntry::Key extension_key = GetKey(extension->id()); EXPECT_TRUE(global_registry()->GetEntryForKey(extension_key)); @@ -245,8 +308,7 @@ scoped_refptr<const extensions::Extension> extension = LoadExtension( test_data_dir_.AppendASCII("api_test/side_panel/simple_default")); ASSERT_TRUE(extension); - SidePanelEntry::Key extension_key = - SidePanelEntry::Key(SidePanelEntry::Id::kExtension, extension->id()); + SidePanelEntry::Key extension_key = GetKey(extension->id()); EXPECT_TRUE(global_registry()->GetEntryForKey(extension_key)); EXPECT_FALSE(side_panel_coordinator()->IsSidePanelShowing()); @@ -269,8 +331,7 @@ extensions::ExtensionSidePanelManager::GetOrCreateForBrowser(browser()) ->GetExtensionCoordinatorForTesting(extension->id()); - SidePanelEntry::Key extension_key = - SidePanelEntry::Key(SidePanelEntry::Id::kExtension, extension->id()); + SidePanelEntry::Key extension_key = GetKey(extension->id()); SidePanelEntry* extension_entry = global_registry()->GetEntryForKey(extension_key); @@ -305,15 +366,15 @@ scoped_refptr<const extensions::Extension> extension = LoadExtension( test_data_dir_.AppendASCII("api_test/side_panel/setoptions_default_tab")); ASSERT_TRUE(extension); - SidePanelEntry::Key extension_key = - SidePanelEntry::Key(SidePanelEntry::Id::kExtension, extension->id()); + SidePanelEntry::Key extension_key = GetKey(extension->id()); EXPECT_FALSE(global_registry()->GetEntryForKey(extension_key)); { // Call setOptions({enabled: true}) and wait for the extension's // SidePanelEntry to be registered. ExtensionSidePanelRegistryWaiter waiter(global_registry(), extension->id()); - RunSetOptions(*extension, "panel_1.html", /*enabled=*/true); + RunSetOptions(*extension, /*tab_id=*/absl::nullopt, "panel_1.html", + /*enabled=*/true); waiter.WaitForRegistration(); } @@ -323,7 +384,8 @@ // Call setOptions({enabled: false}) and wait for the extension's // SidePanelEntry to be deregistered. ExtensionSidePanelRegistryWaiter waiter(global_registry(), extension->id()); - RunSetOptions(*extension, "panel_1.html", /*enabled=*/false); + RunSetOptions(*extension, /*tab_id=*/absl::nullopt, /*path=*/absl::nullopt, + /*enabled=*/false); waiter.WaitForDeregistration(); } @@ -333,7 +395,8 @@ // Sanity check that re-enabling the side panel will register the entry // again and a view with the new side panel path can be shown. ExtensionSidePanelRegistryWaiter waiter(global_registry(), extension->id()); - RunSetOptions(*extension, "panel_2.html", /*enabled=*/true); + RunSetOptions(*extension, /*tab_id=*/absl::nullopt, "panel_2.html", + /*enabled=*/true); waiter.WaitForRegistration(); } @@ -349,7 +412,8 @@ // Calling setOptions({enabled: false}) when the extension's SidePanelEntry // is shown should close the side panel. ExtensionSidePanelRegistryWaiter waiter(global_registry(), extension->id()); - RunSetOptions(*extension, "panel_2.html", /*enabled=*/false); + RunSetOptions(*extension, /*tab_id=*/absl::nullopt, /*path=*/absl::nullopt, + /*enabled=*/false); waiter.WaitForDeregistration(); } @@ -370,13 +434,13 @@ extensions::ExtensionSidePanelManager::GetOrCreateForBrowser(browser()) ->GetExtensionCoordinatorForTesting(extension->id()); - SidePanelEntry::Key extension_key = - SidePanelEntry::Key(SidePanelEntry::Id::kExtension, extension->id()); + SidePanelEntry::Key extension_key = GetKey(extension->id()); EXPECT_TRUE(global_registry()->GetEntryForKey(extension_key)); // Check that the extension's side panel view shows the most recently set // path. - RunSetOptions(*extension, "panel_1.html", /*enabled=*/true); + RunSetOptions(*extension, /*tab_id=*/absl::nullopt, "panel_1.html", + /*enabled=*/true); side_panel_coordinator()->Show(extension_key); ASSERT_TRUE(panel_1_listener.WaitUntilSatisfied()); EXPECT_FALSE(default_path_listener.was_satisfied()); @@ -384,20 +448,15 @@ // Check that changing the path while the view is active will cause the view // to navigate to the new path. - RunSetOptions(*extension, "default_path.html", /*enabled=*/true); + RunSetOptions(*extension, /*tab_id=*/absl::nullopt, "default_path.html", + /*enabled=*/true); ASSERT_TRUE(default_path_listener.WaitUntilSatisfied()); EXPECT_TRUE(side_panel_coordinator()->IsSidePanelShowing()); // Switch to the reading list in the side panel and check that the extension // view is cached (i.e. the view exists but is not shown, and its web contents // still exists). - { - TestSidePanelEntryWaiter reading_list_waiter( - global_registry()->GetEntryForKey( - SidePanelEntry::Key(SidePanelEntry::Id::kReadingList))); - side_panel_coordinator()->Show(SidePanelEntry::Id::kReadingList); - reading_list_waiter.WaitForEntryShown(); - } + ShowEntryAndWait(SidePanelEntry::Key(SidePanelEntry::Id::kReadingList)); EXPECT_TRUE(global_registry()->GetEntryForKey(extension_key)->CachedView()); @@ -408,7 +467,8 @@ // Test calling setOptions with a different path when the extension's view is // cached. The cached view should then be invalidated and its web contents are // destroyed. - RunSetOptions(*extension, "panel_1.html", /*enabled=*/true); + RunSetOptions(*extension, /*tab_id=*/absl::nullopt, "panel_1.html", + /*enabled=*/true); destroyed_watcher.Wait(); // When the extension's entry is shown again, the view with the updated path @@ -426,8 +486,7 @@ scoped_refptr<const extensions::Extension> extension = LoadExtension( test_data_dir_.AppendASCII("api_test/side_panel/simple_default")); ASSERT_TRUE(extension); - SidePanelEntry::Key extension_key = - SidePanelEntry::Key(SidePanelEntry::Id::kExtension, extension->id()); + SidePanelEntry::Key extension_key = GetKey(extension->id()); EXPECT_TRUE(global_registry()->GetEntryForKey(extension_key)); { @@ -492,8 +551,7 @@ scoped_refptr<const extensions::Extension> extension = LoadExtension( test_data_dir_.AppendASCII("api_test/side_panel/simple_default")); ASSERT_TRUE(extension); - SidePanelEntry::Key extension_key = - SidePanelEntry::Key(SidePanelEntry::Id::kExtension, extension->id()); + SidePanelEntry::Key extension_key = GetKey(extension->id()); EXPECT_TRUE(global_registry()->GetEntryForKey(extension_key)); { @@ -525,6 +583,204 @@ EXPECT_FALSE(side_panel_coordinator()->IsSidePanelShowing()); } +// Test that calling sidePanel.setOptions({enabled: false}) for a specific tab +// will hide the extension's global side panel for that tab. +IN_PROC_BROWSER_TEST_F(ExtensionSidePanelBrowserTest, HideGlobalPanelForTab) { + scoped_refptr<const extensions::Extension> extension = LoadExtension( + test_data_dir_.AppendASCII("api_test/side_panel/simple_default")); + ASSERT_TRUE(extension); + + SidePanelEntry::Key extension_key = GetKey(extension->id()); + EXPECT_TRUE(global_registry()->GetEntryForKey(extension_key)); + + // Show the extension's side panel and set a global variable to change the + // state of the side panel's page. + ExtensionTestMessageListener default_path_listener("default_path"); + side_panel_coordinator()->Show(extension_key); + ASSERT_TRUE(default_path_listener.WaitUntilSatisfied()); + EXPECT_TRUE(side_panel_coordinator()->IsSidePanelShowing()); + + SetGlobalVariableInExtensionSidePanel(extension->id(), "altered_state"); + EXPECT_EQ("altered_state", + GetGlobalVariableInExtensionSidePanel(extension->id())); + + // Disable the extension's side panel for the current tab. + DisableForCurrentTab(*extension); + + // Calling sidePanel.setOptions({enabled: true}) for the current tab should + // re-register the entry. + { + ExtensionSidePanelRegistryWaiter waiter(global_registry(), extension->id()); + RunSetOptions(*extension, GetCurrentTabId(), /*path=*/absl::nullopt, + /*enabled=*/true); + waiter.WaitForRegistration(); + EXPECT_TRUE(global_registry()->GetEntryForKey(extension_key)); + EXPECT_FALSE(side_panel_coordinator()->IsSidePanelShowing()); + } + + // Show the side panel entry and check its state to verify that it's the same + // page as before. + ShowEntryAndWait(extension_key); + EXPECT_EQ("altered_state", + GetGlobalVariableInExtensionSidePanel(extension->id())); + + // Disable the extension's side panel for the current tab again. + DisableForCurrentTab(*extension); + + // Open a new tab and navigate to it. The extension's side panel should be + // available again since it's not disabled for the new tab. + { + ExtensionSidePanelRegistryWaiter waiter(global_registry(), extension->id()); + ui_test_utils::NavigateToURLWithDisposition( + browser(), GURL("http://example.com"), + WindowOpenDisposition::NEW_FOREGROUND_TAB, + ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB); + ASSERT_EQ(2, browser()->tab_strip_model()->count()); + ASSERT_TRUE(browser()->tab_strip_model()->IsTabSelected(1)); + + waiter.WaitForRegistration(); + EXPECT_TRUE(global_registry()->GetEntryForKey(extension_key)); + EXPECT_FALSE(side_panel_coordinator()->IsSidePanelShowing()); + } + + // Show the side panel entry and check its state to verify that it's the same + // page as before. + ShowEntryAndWait(extension_key); + EXPECT_EQ("altered_state", + GetGlobalVariableInExtensionSidePanel(extension->id())); + + // Go back to the first tab where the side panel is disabled and verify the + // extension's side panel is no longer there. + { + ExtensionSidePanelRegistryWaiter waiter(global_registry(), extension->id()); + browser()->tab_strip_model()->ActivateTabAt(0); + waiter.WaitForDeregistration(); + EXPECT_FALSE(global_registry()->GetEntryForKey(extension_key)); + EXPECT_FALSE(side_panel_coordinator()->IsSidePanelShowing()); + } +} + +// Test that the saved view state for the hidden global extension side panel is +// invalidated if setOptions({enabled: false}) is called without a tab ID. +IN_PROC_BROWSER_TEST_F(ExtensionSidePanelBrowserTest, + DisableGlobalPanelWhileHidden) { + scoped_refptr<const extensions::Extension> extension = LoadExtension( + test_data_dir_.AppendASCII("api_test/side_panel/simple_default")); + ASSERT_TRUE(extension); + auto* extension_coordinator = + extensions::ExtensionSidePanelManager::GetOrCreateForBrowser(browser()) + ->GetExtensionCoordinatorForTesting(extension->id()); + + SidePanelEntry::Key extension_key = GetKey(extension->id()); + EXPECT_TRUE(global_registry()->GetEntryForKey(extension_key)); + + // Show the extension's side panel. + ExtensionTestMessageListener default_path_listener("default_path"); + side_panel_coordinator()->Show(extension_key); + ASSERT_TRUE(default_path_listener.WaitUntilSatisfied()); + EXPECT_TRUE(side_panel_coordinator()->IsSidePanelShowing()); + + // Disable the extension's side panel for the current tab. + { + ExtensionSidePanelRegistryWaiter waiter(global_registry(), extension->id()); + RunSetOptions(*extension, GetCurrentTabId(), /*path=*/absl::nullopt, + /*enabled=*/false); + waiter.WaitForDeregistration(); + EXPECT_FALSE(global_registry()->GetEntryForKey(extension_key)); + EXPECT_FALSE(side_panel_coordinator()->IsSidePanelShowing()); + } + + // There should be web contents from the saved view. + ASSERT_TRUE(extension_coordinator->GetHostWebContentsForTesting()); + content::WebContentsDestroyedWatcher destroyed_watcher( + extension_coordinator->GetHostWebContentsForTesting()); + + // Calling setOptions({enabled: false}) for all tabs should destroy the + // contents. + RunSetOptions(*extension, /*tab_id=*/absl::nullopt, /*path=*/absl::nullopt, + /*enabled=*/false); + destroyed_watcher.Wait(); + + // Sanity check that calling setOptions({enabled: true}) for all tabs while on + // a tab where the panel is disabled should be a no-op. + RunSetOptions(*extension, /*tab_id=*/absl::nullopt, "default_path.html", + /*enabled=*/true); + EXPECT_FALSE(global_registry()->GetEntryForKey(extension_key)); + + // Open a new tab and navigate to it. The extension's side panel should be + // available again since it's not disabled for the new tab. + { + ExtensionSidePanelRegistryWaiter waiter(global_registry(), extension->id()); + ui_test_utils::NavigateToURLWithDisposition( + browser(), GURL("http://example.com"), + WindowOpenDisposition::NEW_FOREGROUND_TAB, + ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB); + ASSERT_EQ(2, browser()->tab_strip_model()->count()); + ASSERT_TRUE(browser()->tab_strip_model()->IsTabSelected(1)); + + waiter.WaitForRegistration(); + EXPECT_TRUE(global_registry()->GetEntryForKey(extension_key)); + EXPECT_FALSE(side_panel_coordinator()->IsSidePanelShowing()); + } +} + +// Test that when the extension's side panel is shown, switching from a tab +// where the panel is enabled to one where it's disabled then back to the first +// tab will re-register the entry but not show it. This behavior is a little +// weird, but trying to have it reopen causes far more complexity than is +// worthwhile. +IN_PROC_BROWSER_TEST_F(ExtensionSidePanelBrowserTest, ReEnabledPanelNotShown) { + // Open a second tab and switch back to the first tab. + ui_test_utils::NavigateToURLWithDisposition( + browser(), GURL("http://example.com"), + WindowOpenDisposition::NEW_FOREGROUND_TAB, + ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB); + ASSERT_EQ(2, browser()->tab_strip_model()->count()); + ASSERT_TRUE(browser()->tab_strip_model()->IsTabSelected(1)); + + int second_tab_id = GetCurrentTabId(); + browser()->tab_strip_model()->ActivateTabAt(0); + + scoped_refptr<const extensions::Extension> extension = LoadExtension( + test_data_dir_.AppendASCII("api_test/side_panel/simple_default")); + ASSERT_TRUE(extension); + + SidePanelEntry::Key extension_key = GetKey(extension->id()); + EXPECT_TRUE(global_registry()->GetEntryForKey(extension_key)); + + // Show the extension's side panel. + ExtensionTestMessageListener default_path_listener("default_path"); + side_panel_coordinator()->Show(extension_key); + ASSERT_TRUE(default_path_listener.WaitUntilSatisfied()); + EXPECT_TRUE(side_panel_coordinator()->IsSidePanelShowing()); + + // Disable the extension's side panel for the second tab, which shouldn't do + // anything here since we're on the first tab. + RunSetOptions(*extension, second_tab_id, /*path=*/absl::nullopt, + /*enabled=*/false); + EXPECT_TRUE(side_panel_coordinator()->IsSidePanelShowing()); + + // Switch to the second tab and verify that the extension's entry is no longer + // registered. + { + ExtensionSidePanelRegistryWaiter waiter(global_registry(), extension->id()); + browser()->tab_strip_model()->ActivateTabAt(1); + waiter.WaitForDeregistration(); + EXPECT_FALSE(global_registry()->GetEntryForKey(extension_key)); + EXPECT_FALSE(side_panel_coordinator()->IsSidePanelShowing()); + } + + // Switch back to the first tab and verify that the extension's entry is + // registered again but is not showing. + { + ExtensionSidePanelRegistryWaiter waiter(global_registry(), extension->id()); + browser()->tab_strip_model()->ActivateTabAt(0); + waiter.WaitForRegistration(); + EXPECT_TRUE(global_registry()->GetEntryForKey(extension_key)); + EXPECT_FALSE(side_panel_coordinator()->IsSidePanelShowing()); + } +} + class ExtensionSidePanelDisabledBrowserTest : public ExtensionBrowserTest { public: ExtensionSidePanelDisabledBrowserTest() { @@ -550,8 +806,7 @@ scoped_refptr<const extensions::Extension> extension = LoadExtension( test_data_dir_.AppendASCII("api_test/side_panel/simple_default")); ASSERT_TRUE(extension); - SidePanelEntry::Key extension_key = - SidePanelEntry::Key(SidePanelEntry::Id::kExtension, extension->id()); + SidePanelEntry::Key extension_key = GetKey(extension->id()); EXPECT_FALSE(global_registry()->GetEntryForKey(extension_key)); }
diff --git a/chrome/browser/ui/views/side_panel/extensions/extension_side_panel_coordinator.cc b/chrome/browser/ui/views/side_panel/extensions/extension_side_panel_coordinator.cc index 588336d..1610666 100644 --- a/chrome/browser/ui/views/side_panel/extensions/extension_side_panel_coordinator.cc +++ b/chrome/browser/ui/views/side_panel/extensions/extension_side_panel_coordinator.cc
@@ -5,6 +5,7 @@ #include "chrome/browser/ui/views/side_panel/extensions/extension_side_panel_coordinator.h" #include "base/strings/utf_string_conversions.h" +#include "chrome/browser/extensions/extension_tab_util.h" #include "chrome/browser/extensions/extension_view_host_factory.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" @@ -12,6 +13,7 @@ #include "chrome/browser/ui/views/side_panel/side_panel_coordinator.h" #include "chrome/browser/ui/views/side_panel/side_panel_registry.h" #include "chrome/common/extensions/api/side_panel.h" +#include "content/public/browser/browser_context.h" #include "content/public/browser/web_contents.h" #include "extensions/browser/extension_icon_placeholder.h" #include "extensions/common/constants.h" @@ -23,6 +25,24 @@ namespace extensions { +namespace { + +int GetCurrentTabId(Browser* browser) { + return ExtensionTabUtil::GetTabId( + browser->tab_strip_model()->GetActiveWebContents()); +} + +bool HasGlobalSidePanel(content::BrowserContext* context, + const Extension& extension) { + auto options = SidePanelService::Get(context)->GetOptions( + extension, /*tab_id=*/absl::nullopt); + + return options.enabled.has_value() && *options.enabled && + options.path.has_value(); +} + +} // namespace + ExtensionSidePanelCoordinator::ExtensionSidePanelCoordinator( Browser* browser, const Extension* extension, @@ -40,6 +60,7 @@ // `service` can be null for some tests. if (service) { scoped_service_observation_.Observe(service); + browser_->tab_strip_model()->AddObserver(this); LoadExtensionIcon(); auto default_options = service->GetOptions(*extension, /*tab_id=*/absl::nullopt); @@ -69,8 +90,26 @@ return SidePanelEntry::Key(SidePanelEntry::Id::kExtension, extension_->id()); } +SidePanelEntry* ExtensionSidePanelCoordinator::GetEntry() const { + return global_registry_->GetEntryForKey(GetEntryKey()); +} + +bool ExtensionSidePanelCoordinator::IsDisabledForTab(int tab_id) const { + auto options = SidePanelService::Get(browser_->profile()) + ->GetOptions(*extension_, tab_id); + return options.enabled.has_value() && !(*options.enabled); +} + void ExtensionSidePanelCoordinator::DeregisterGlobalEntry() { global_registry_->Deregister(GetEntryKey()); + global_entry_view_.reset(); +} + +void ExtensionSidePanelCoordinator::DeregisterGlobalEntryAndCacheView() { + if (GetEntry()) { + global_entry_view_ = + global_registry_->DeregisterAndReturnView(GetEntryKey()); + } } void ExtensionSidePanelCoordinator::OnPanelOptionsChanged( @@ -81,8 +120,31 @@ return; } - // TODO(crbug.com/1378048): Handle tab specific side panel options. + bool should_enable_entry = + updated_options.enabled.has_value() && *updated_options.enabled; + bool should_disable_entry = + updated_options.enabled.has_value() && !(*updated_options.enabled); + SidePanelEntry* entry = GetEntry(); + + // TODO(crbug.com/1378048): Handle enabling tab specific side panel views if + // `updated_options.tab_id` is specified. if (updated_options.tab_id.has_value()) { + if (GetCurrentTabId(browser_) == *updated_options.tab_id) { + if (!entry && should_enable_entry && + HasGlobalSidePanel(browser_->profile(), *extension_)) { + // We create an entry if: + // - The side panel is being enabled/no longer being disabled for this + // tab + // - The extension has a global side panel specified + // - There is currently no global entry registered + CreateAndRegisterEntry(); + } else if (should_disable_entry) { + // if the side panel is being disabled for this tab and there exists an + // entry, deregister it and keep its view. + DeregisterGlobalEntryAndCacheView(); + } + } + return; } @@ -90,23 +152,26 @@ GURL previous_url = side_panel_url_; if (updated_options.path.has_value()) { side_panel_url_ = extension_->GetResourceURL(*updated_options.path); + if (previous_url != side_panel_url_) { + global_entry_view_.reset(); + } } // Deregister the SidePanelEntry if `enabled` is false. - if (updated_options.enabled.has_value() && !(*updated_options.enabled)) { + if (should_disable_entry) { DeregisterGlobalEntry(); return; } - // If there is no entry for this extension and `enabled` is true, create and - // register the entry. - SidePanelEntry::Key key = GetEntryKey(); - auto* entry = global_registry_->GetEntryForKey(key); - if (!entry) { + bool should_create_entry = !entry && should_enable_entry && + !IsDisabledForTab(GetCurrentTabId(browser_)); + if (should_create_entry) { + // Create a global entry if the extension has not disabled its side panel + // for the current tab. CreateAndRegisterEntry(); - } else if (previous_url != side_panel_url_) { - if (global_registry_->active_entry().has_value() && - (*global_registry_->active_entry())->key() == key) { + } else if (entry && previous_url != side_panel_url_) { + // Handle changes to the side panel's url if an entry exists. + if (global_registry_->active_entry() == entry) { // If this extension's entry is active, navigate the entry's view to the // updated URL. NavigateIfNecessary(); @@ -143,6 +208,34 @@ } } +void ExtensionSidePanelCoordinator::OnTabStripModelChanged( + TabStripModel* tab_strip_model, + const TabStripModelChange& change, + const TabStripSelectionChange& selection) { + // Registering/deregistering an entry should only happen if the active tab + // changes and the extension has specified a global side panel. + if (!selection.active_tab_changed() || + !HasGlobalSidePanel(browser_->profile(), *extension_)) { + return; + } + + bool disabled_for_old_tab = + IsDisabledForTab(ExtensionTabUtil::GetTabId(selection.old_contents)); + bool disabled_for_new_tab = + IsDisabledForTab(ExtensionTabUtil::GetTabId(selection.new_contents)); + + if (!disabled_for_old_tab && disabled_for_new_tab) { + // If we switch to a tab where the extension's global side panel is + // disabled, deregister the entry but keep its view. + DeregisterGlobalEntryAndCacheView(); + } else if (disabled_for_old_tab && !disabled_for_new_tab) { + // If we switch to a tab where the extension's global side panel is enabled, + // re-register the entry. + DCHECK(!GetEntry()); + CreateAndRegisterEntry(); + } +} + void ExtensionSidePanelCoordinator::CreateAndRegisterEntry() { // The extension icon should be initialized in the constructor, so this should // not be null. @@ -160,6 +253,11 @@ } std::unique_ptr<views::View> ExtensionSidePanelCoordinator::CreateView() { + if (global_entry_view_) { + DCHECK(host_); + return std::move(global_entry_view_); + } + host_ = ExtensionViewHostFactory::CreateSidePanelHost(side_panel_url_, browser_);
diff --git a/chrome/browser/ui/views/side_panel/extensions/extension_side_panel_coordinator.h b/chrome/browser/ui/views/side_panel/extensions/extension_side_panel_coordinator.h index dafd6c2..51319128 100644 --- a/chrome/browser/ui/views/side_panel/extensions/extension_side_panel_coordinator.h +++ b/chrome/browser/ui/views/side_panel/extensions/extension_side_panel_coordinator.h
@@ -9,6 +9,7 @@ #include "base/scoped_observation.h" #include "chrome/browser/extensions/api/side_panel/side_panel_service.h" #include "chrome/browser/extensions/extension_view_host.h" +#include "chrome/browser/ui/tabs/tab_strip_model_observer.h" #include "chrome/browser/ui/views/extensions/extension_view_views.h" #include "chrome/browser/ui/views/side_panel/side_panel_entry.h" #include "chrome/browser/ui/views/side_panel/side_panel_view_state_observer.h" @@ -35,7 +36,8 @@ // shown if this extension's SidePanelEntry is active. class ExtensionSidePanelCoordinator : public ExtensionViewViews::Observer, public IconImage::Observer, - public SidePanelService::Observer { + public SidePanelService::Observer, + public TabStripModelObserver { public: explicit ExtensionSidePanelCoordinator(Browser* browser, const Extension* extension, @@ -57,10 +59,20 @@ private: SidePanelEntry::Key GetEntryKey() const; + SidePanelEntry* GetEntry() const; + + // Returns if this extension's side panel is explicitly disabled for the given + // `tab_id`. + bool IsDisabledForTab(int tab_id) const; + // Deregisters this extension's SidePanelEntry from the global // SidePanelCoordinator. void DeregisterGlobalEntry(); + // Deregisters this extension's SidePanelEntry from the global + // SidePanelCoordinator and caches the entry's view into `global_entry_view_`. + void DeregisterGlobalEntryAndCacheView(); + // SidePanelService::Observer: void OnPanelOptionsChanged( const ExtensionId& extension_id, @@ -73,6 +85,12 @@ // IconImage::Observer void OnExtensionIconImageChanged(IconImage* image) override; + // TabStripModelObserver: + void OnTabStripModelChanged( + TabStripModel* tab_strip_model, + const TabStripModelChange& change, + const TabStripSelectionChange& selection) override; + // Creates and registers the SidePanelEntry for this extension, and observes // the entry. This is called if the extension has a default side panel path // when the browser view is created or when the extension is loaded. @@ -113,6 +131,10 @@ // The extension's own icon for its side panel entry. std::unique_ptr<IconImage> extension_icon_; + // Cached view for global entry if it was disabled for a specific tab and may + // be shown again on a different tab where it's enabled. + std::unique_ptr<views::View> global_entry_view_; + base::ScopedObservation<ExtensionViewViews, ExtensionViewViews::Observer> scoped_view_observation_{this}; base::ScopedObservation<SidePanelService, SidePanelService::Observer>
diff --git a/chrome/browser/ui/views/side_search/side_search_icon_view.cc b/chrome/browser/ui/views/side_search/side_search_icon_view.cc index e3b2fdd..605dca17 100644 --- a/chrome/browser/ui/views/side_search/side_search_icon_view.cc +++ b/chrome/browser/ui/views/side_search/side_search_icon_view.cc
@@ -24,6 +24,7 @@ #include "components/vector_icons/vector_icons.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/metadata/metadata_impl_macros.h" +#include "ui/base/ui_base_features.h" #include "ui/gfx/paint_vector_icon.h" #include "ui/views/view_class_properties.h" @@ -148,7 +149,9 @@ const gfx::VectorIcon& SideSearchIconView::GetVectorIcon() const { // Default to the kSearchIcon if the DSE icon image is not available. - return vector_icons::kSearchIcon; + return features::IsChromeRefresh2023() + ? vector_icons::kSearchChromeRefreshIcon + : vector_icons::kSearchIcon; } ui::ImageModel SideSearchIconView::GetSizedIconImage(int size) const {
diff --git a/chrome/browser/ui/webui/ash/parent_access/parent_access_ui.cc b/chrome/browser/ui/webui/ash/parent_access/parent_access_ui.cc index c3ad1ca..7176199a 100644 --- a/chrome/browser/ui/webui/ash/parent_access/parent_access_ui.cc +++ b/chrome/browser/ui/webui/ash/parent_access/parent_access_ui.cc
@@ -83,6 +83,8 @@ IDR_LOCAL_WEB_APPROVALS_AFTER_JS); source->AddResourcePath("flows/extension_approvals_disabled.js", IDR_EXTENSION_APPROVALS_DISABLED_JS); + source->AddResourcePath("flows/extension_approvals_before.js", + IDR_EXTENSION_APPROVALS_BEFORE_JS); source->AddResourcePath("parent_access_before.js", IDR_PARENT_ACCESS_BEFORE_JS); source->AddResourcePath("parent_access_disabled.js", @@ -91,6 +93,7 @@ IDR_PARENT_ACCESS_UI_MOJOM_WEBUI_JS); source->AddResourcePath("webview_manager.js", IDR_PARENT_ACCESS_WEBVIEW_MANAGER_JS); + source->AddResourcePath("utils.js", IDR_PARENT_ACCESS_UTILS_JS); source->AddResourcePath("parent_access_screen.js", IDR_PARENT_ACCESS_SCREEN_JS); source->AddResourcePaths( @@ -124,7 +127,15 @@ {"extensionApprovalsDisabledTitle", IDS_PARENT_ACCESS_EXTENSION_APPROVALS_DISABLED_TITLE}, {"extensionApprovalsDisabledSubtitle", - IDS_PARENT_ACCESS_EXTENSION_APPROVALS_DISABLED_SUBTITLE}}; + IDS_PARENT_ACCESS_EXTENSION_APPROVALS_DISABLED_SUBTITLE}, + {"extensionApprovalsAddExtensionBeforeTitle", + IDS_PARENT_ACCESS_EXTENSION_APPROVALS_ADD_EXTENSION_BEFORE_TITLE}, + {"extensionApprovalsAddExtensionBeforeSubtitle", + IDS_PARENT_ACCESS_EXTENSION_APPROVALS_ADD_EXTENSION_BEFORE_SUBTITLE}, + {"extensionApprovalsEnableExtensionBeforeTitle", + IDS_PARENT_ACCESS_EXTENSION_APPROVALS_ENABLE_EXTENSION_BEFORE_TITLE}, + {"extensionApprovalsEnableExtensionBeforeSubtitle", + IDS_PARENT_ACCESS_EXTENSION_APPROVALS_ENABLE_EXTENSION_BEFORE_SUBTITLE}}; source->AddLocalizedStrings(kLocalizedStrings); // Enables use of test_loader.html
diff --git a/chrome/browser/ui/webui/ash/parent_access/parent_access_ui.mojom b/chrome/browser/ui/webui/ash/parent_access/parent_access_ui.mojom index 9e1aea7..40fb1c62 100644 --- a/chrome/browser/ui/webui/ash/parent_access/parent_access_ui.mojom +++ b/chrome/browser/ui/webui/ash/parent_access/parent_access_ui.mojom
@@ -65,8 +65,29 @@ array<uint8> favicon_png_bytes; }; +// Parameters for the local extension approvals V2 feature. +struct ExtensionApprovalsParams { + enum ExtensionApprovalType { + kAdd, // Adding a new extension + kEnable, // Enabling an existing extension + }; + + // The type of approval being requested. + ExtensionApprovalType approval_type; + + // Display name for the extension. + mojo_base.mojom.String16 extension_name; + + // Extension icon, stored as an array of bytes. + array<uint8> icon_png_bytes; + + // The child's name to be displayed to the parent. + mojo_base.mojom.String16 child_display_name; +}; + union FlowTypeParams { WebApprovalsParams web_approvals_params; + ExtensionApprovalsParams extension_approvals_params; }; // The result of the parent access request.
diff --git a/chrome/browser/usb/usb_chooser_controller.cc b/chrome/browser/usb/usb_chooser_controller.cc index 0b1666b..eb4eb3c 100644 --- a/chrome/browser/usb/usb_chooser_controller.cc +++ b/chrome/browser/usb/usb_chooser_controller.cc
@@ -90,10 +90,8 @@ RenderFrameHost* render_frame_host, std::vector<device::mojom::UsbDeviceFilterPtr> device_filters, blink::mojom::WebUsbService::GetPermissionCallback callback) - : ChooserController(CreateExtensionAwareChooserTitle( - render_frame_host, - IDS_USB_DEVICE_CHOOSER_PROMPT_ORIGIN, - IDS_USB_DEVICE_CHOOSER_PROMPT_EXTENSION_NAME)), + : ChooserController( + CreateChooserTitle(render_frame_host, IDS_USB_DEVICE_CHOOSER_PROMPT)), filters_(std::move(device_filters)), callback_(std::move(callback)), requesting_frame_(render_frame_host) {
diff --git a/chrome/browser/webapps/web_app_offline_browsertest.cc b/chrome/browser/webapps/web_app_offline_browsertest.cc index abdc1c7..784f5f2 100644 --- a/chrome/browser/webapps/web_app_offline_browsertest.cc +++ b/chrome/browser/webapps/web_app_offline_browsertest.cc
@@ -28,12 +28,8 @@ #include "base/win/windows_version.h" #endif -#if BUILDFLAG(IS_CHROMEOS) -#include "chromeos/constants/chromeos_features.h" -#endif - #if BUILDFLAG(IS_CHROMEOS_ASH) -#include "ash/constants/ash_features.h" +#include "ash/public/cpp/style/dark_light_mode_controller.h" #endif using ::testing::ElementsAre; @@ -496,14 +492,6 @@ public: WebAppOfflineDarkModeTest() { std::vector<base::test::FeatureRef> disabled_features; -#if BUILDFLAG(IS_CHROMEOS) - disabled_features.push_back(chromeos::features::kDarkLightMode); -#endif - -#if BUILDFLAG(IS_CHROMEOS_ASH) - disabled_features.push_back(ash::features::kNotificationsRefresh); -#endif - feature_list_.InitWithFeatures({features::kPWAsDefaultOfflinePage, blink::features::kWebAppEnableDarkMode}, {disabled_features}); @@ -524,6 +512,15 @@ #endif // BUILDFLAG(IS_MAC) } + void SetUpOnMainThread() override { +#if BUILDFLAG(IS_CHROMEOS_ASH) + // Explicitly set dark mode in ChromeOS or we can't get light mode after + // sunset (due to dark mode auto-scheduling). + ash::DarkLightModeController::Get()->SetDarkModeEnabledForTest( + GetParam() == blink::mojom::PreferredColorScheme::kDark); +#endif + } + protected: void SetUpCommandLine(base::CommandLine* command_line) override { // ShellContentBrowserClient::OverrideWebkitPrefs() overrides the
diff --git a/chrome/browser/win/conflicts/msi_util.cc b/chrome/browser/win/conflicts/msi_util.cc index 683a7ae..8b3e650 100644 --- a/chrome/browser/win/conflicts/msi_util.cc +++ b/chrome/browser/win/conflicts/msi_util.cc
@@ -123,13 +123,17 @@ // Internally, the Microsoft Installer uses a special formatting of the guids // to store the information in the registry. product_guid = product_guid.substr(1, 36); - if (!base::IsValidGUID(base::AsStringPiece16(product_guid))) + if (!base::GUID::ParseCaseInsensitive(base::AsStringPiece16(product_guid)) + .is_valid()) { return false; + } std::wstring product_squid = InstallUtil::GuidToSquid(product_guid); component_guid = component_guid.substr(1, 36); - if (!base::IsValidGUID(base::AsStringPiece16(component_guid))) + if (!base::GUID::ParseCaseInsensitive(base::AsStringPiece16(component_guid)) + .is_valid()) { return false; + } std::wstring component_squid = InstallUtil::GuidToSquid(component_guid); std::vector<std::wstring> sids = {
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt index d6b77497..f5d7d14 100644 --- a/chrome/build/mac-arm.pgo.txt +++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@ -chrome-mac-arm-main-1679947177-fb316e82e3ba3c9b100a4d9e87e5e8c76b5abc41.profdata +chrome-mac-arm-main-1679954386-05b1b656ba2d69b02ce785f9a11a387b8c09d45c.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt index 1da1b6f7..7ddc8955 100644 --- a/chrome/build/win32.pgo.txt +++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@ -chrome-win32-main-1679929060-34f29b821ee8e423dbbb3d45b09b0227d8306c7d.profdata +chrome-win32-main-1679950782-683238c0416bcc31133aa08de790f1a9ba83313b.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt index 30c09f5..68c1c55 100644 --- a/chrome/build/win64.pgo.txt +++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@ -chrome-win64-main-1679929060-4c299167367e97d7cb321c6c0cf68fb0c992c785.profdata +chrome-win64-main-1679939715-ef406d1f8b818e5b00d81bb8d0eae130ed5c3687.profdata
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc index a6932bf..a4c99fd 100644 --- a/chrome/common/chrome_features.cc +++ b/chrome/common/chrome_features.cc
@@ -319,10 +319,10 @@ // deprecation. BASE_FEATURE(kKeepForceInstalledPreinstalledApps, "KeepForceInstalledPreinstalledApps", - base::FEATURE_ENABLED_BY_DEFAULT); + base::FEATURE_DISABLED_BY_DEFAULT); // Controls if the 'launch anyways' button is shown. const base::FeatureParam<bool> kChromeAppsDeprecationHideLaunchAnyways{ - &kChromeAppsDeprecation, "HideLaunchAnyways", false}; + &kChromeAppsDeprecation, "HideLaunchAnyways", true}; #endif // Enables notification permission revocation for origins that may send
diff --git a/chrome/common/extensions/api/file_manager_private.idl b/chrome/common/extensions/api/file_manager_private.idl index 8c71835..2e94920 100644 --- a/chrome/common/extensions/api/file_manager_private.idl +++ b/chrome/common/extensions/api/file_manager_private.idl
@@ -1098,6 +1098,10 @@ // The files affected by the IOTask. Currently only returned for TrashIOTask. [instanceOf=Entry] object[]? outputs; + + // Volume id of the destination for operations that transfer files to a + // directory (e.g. copy or move). + DOMString destinationVolumeId; }; dictionary DlpMetadata {
diff --git a/chrome/test/data/webui/chromeos/parent_access/parent_access_app_test.js b/chrome/test/data/webui/chromeos/parent_access/parent_access_app_test.js index dd18e223..d83e60c4 100644 --- a/chrome/test/data/webui/chromeos/parent_access/parent_access_app_test.js +++ b/chrome/test/data/webui/chromeos/parent_access/parent_access_app_test.js
@@ -83,10 +83,20 @@ document.body.appendChild(parentAccessApp); await flushTasks(); - // Verify online flow is showing and switch to the after screen. + // Verify before flow is showing and switch to the authentication + // screen. assertEquals(parentAccessApp.currentScreen_, Screens.BEFORE_FLOW); - parentAccessApp.dispatchEvent( - new CustomEvent('show-authentication-flow')); + // Verify the extension approvals before screen is showing. + const parentAccessBefore = + parentAccessApp.shadowRoot.querySelector('parent-access-before'); + const extensionApprovalsBefore = + parentAccessBefore.shadowRoot.querySelector( + 'extension-approvals-before'); + assertNotEquals(null, extensionApprovalsBefore); + + const askParentButton = + parentAccessBefore.shadowRoot.querySelector('.action-button'); + askParentButton.click(); await flushTasks(); // Verify online flow is showing and switch to the after screen.
diff --git a/chrome/test/data/webui/chromeos/parent_access/parent_access_test_utils.js b/chrome/test/data/webui/chromeos/parent_access/parent_access_test_utils.js index 175497c4..3d630e3c 100644 --- a/chrome/test/data/webui/chromeos/parent_access/parent_access_test_utils.js +++ b/chrome/test/data/webui/chromeos/parent_access/parent_access_test_utils.js
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import {ParentAccessParams, ParentAccessParams_FlowType, WebApprovalsParams} from 'chrome://parent-access/parent_access_ui.mojom-webui.js'; +import {ExtensionApprovalsParams, ExtensionApprovalsParams_ExtensionApprovalType, ParentAccessParams, ParentAccessParams_FlowType, WebApprovalsParams} from 'chrome://parent-access/parent_access_ui.mojom-webui.js'; function strToMojoString16(str) { return {data: str.split('').map(ch => ch.charCodeAt(0))}; @@ -30,5 +30,12 @@ const parentAccessParams = new ParentAccessParams(); parentAccessParams.flowType = ParentAccessParams_FlowType.kExtensionAccess; parentAccessParams.isDisabled = isDisabled; + const extensionApprovalsParams = new ExtensionApprovalsParams(); + extensionApprovalsParams.approvalType = + ExtensionApprovalsParams_ExtensionApprovalType.kAdd; + extensionApprovalsParams.extensionName = strToMojoString16('Extension name'); + extensionApprovalsParams.iconPngBytes = []; + extensionApprovalsParams.childDisplayName = strToMojoString16('Child Name'); + parentAccessParams.flowTypeParams = {extensionApprovalsParams}; return parentAccessParams; }
diff --git a/chrome/test/data/webui/css/BUILD.gn b/chrome/test/data/webui/css/BUILD.gn index 028513e0..c7210937 100644 --- a/chrome/test/data/webui/css/BUILD.gn +++ b/chrome/test/data/webui/css/BUILD.gn
@@ -14,4 +14,7 @@ if (is_chromeos_ash) { files += [ "color_provider_css_colors_test_chromeos.ts" ] } + + ts_deps = [ "//ui/webui/resources/js:build_ts" ] + ts_tsconfig_base = "tsconfig_base.json" }
diff --git a/chrome/test/data/webui/css/css_browsertest.js b/chrome/test/data/webui/css/css_browsertest.js index ec5454f..3d91cdfa 100644 --- a/chrome/test/data/webui/css/css_browsertest.js +++ b/chrome/test/data/webui/css/css_browsertest.js
@@ -4,6 +4,7 @@ GEN('#include "build/chromeos_buildflags.h"'); GEN('#include "content/public/test/browser_test.h"'); +GEN('#include "ui/base/ui_base_features.h"'); var CssTest = class extends testing.Test { /** @override */ @@ -30,7 +31,22 @@ }; TEST_F('TextDefaultsTest', 'All', function() { - mocha.run(); + runMochaSuite('TextDefaults') +}); + +var TextDefaultsSystemFontTest = class extends TextDefaultsTest { + /** @override */ + get featureList() { + return { + enabled: [ + 'features::kWebUiSystemFont', + ], + }; + } +} + +TEST_F('TextDefaultsSystemFontTest', 'All', function() { + runMochaSuite('TextDefaultsSystemFont') }); var ColorProviderCSSColorsTest = class extends CssTest {
diff --git a/chrome/test/data/webui/css/text_defaults_test.ts b/chrome/test/data/webui/css/text_defaults_test.ts index a2cb0dc..a6ebfc7 100644 --- a/chrome/test/data/webui/css/text_defaults_test.ts +++ b/chrome/test/data/webui/css/text_defaults_test.ts
@@ -2,42 +2,126 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import {assertNotEquals, assertTrue} from 'chrome://webui-test/chai_assert.js'; +import {PromiseResolver} from 'chrome://resources/js/promise_resolver.js'; +import {assertTrue} from 'chrome://webui-test/chai_assert.js'; + +function getExpectedFontFamily(expectingSystemFont: boolean): string { + if (!expectingSystemFont) { + return 'Roboto'; + } + + const fontFamily = + // <if expr="is_linux"> + '"DejaVu Sans"'; + // </if> + // <if expr="is_macosx"> + 'system-ui'; + // </if> + // <if expr="is_win"> + '"Segoe UI"'; + // </if> + // <if expr="chromeos_ash"> + 'Roboto'; + // </if> + // <if expr="chromeos_lacros"> + // TODO(crbug.com/1427641): Change to 'Roboto' once bug is fixed. + 'sans'; + // </if> + // <if expr="is_fuchsia"> + // TODO(dpapad): WebUI tests are compiled on Fuchsia but don't seem to run + // on any bot, so the value below does not matter, it just makes the code + // syntactically valid. Figure out whether the tests should be run, or + // excluded from compilation on Fuchsia. + 'unknown'; + // </if> + + return fontFamily; +} + +// Asserts that a CSS rule specifying font-family with the expected value +// exists. +function assertFontFamilyRule( + link: HTMLLinkElement, expectingSystemFont: boolean) { + assertTrue(!!link.sheet); + const styleRules = + Array.from(link.sheet.cssRules).filter(r => r instanceof CSSStyleRule) as + CSSStyleRule[]; + assertTrue(styleRules.length > 0); + + const fontFamily = styleRules[0]!.style.getPropertyValue('font-family'); + const expectedFontFamily = getExpectedFontFamily(expectingSystemFont); + assertTrue( + fontFamily.startsWith(expectedFontFamily), + `Found: '${fontFamily.toString()}'`); +} + +// Asserts that a 'div' inherits the expected font-family value. +function assertFontFamilyApplied(expectingSystemFont: boolean) { + const div = document.createElement('div'); + div.textContent = 'Dummy text'; + document.body.appendChild(div); + + const fontFamily = div.computedStyleMap().get('font-family'); + assertTrue(!!fontFamily); + const expectedFontFamily = getExpectedFontFamily(expectingSystemFont); + assertTrue( + fontFamily.toString().startsWith(expectedFontFamily), + `Found: '${fontFamily.toString()}'`); +} + + +function testFontFamily( + cssFile: string, expectingSystemFont: boolean): Promise<void> { + const resolver = new PromiseResolver<void>(); + + const link = document.createElement('link'); + link.rel = 'stylesheet'; + link.href = cssFile; + + link.onload = function() { + assertFontFamilyRule(link, expectingSystemFont); + assertFontFamilyApplied(expectingSystemFont); + resolver.resolve(); + }; + document.body.appendChild(link); + + return resolver.promise; +} suite('TextDefaults', function() { setup(function() { document.body.innerHTML = window.trustedTypes!.emptyHTML; }); - function assertFontFamilyExists(link: HTMLLinkElement) { - assertTrue(!!link.sheet); - const styleRules = - Array.from(link.sheet.cssRules) - .filter(r => r instanceof CSSStyleRule) as CSSStyleRule[]; - assertTrue(styleRules.length > 0); - const fontFamily = styleRules[0]!.style.getPropertyValue('font-family'); - assertNotEquals('', fontFamily); - } - - test('text_defaults.css', function(done) { - const link = document.createElement('link'); - link.rel = 'stylesheet'; - link.href = 'chrome://resources/css/text_defaults.css'; - link.onload = function() { - assertFontFamilyExists(link); - done(); - }; - document.body.appendChild(link); + test('text_defaults.css', function() { + return testFontFamily( + 'chrome://resources/css/text_defaults.css', + true /*expectingSystemFont*/); }); - test('text_defaults_md.css', function(done) { - const link = document.createElement('link'); - link.rel = 'stylesheet'; - link.href = 'chrome://resources/css/text_defaults_md.css'; - link.onload = function() { - assertFontFamilyExists(link); - done(); - }; - document.body.appendChild(link); + test('text_defaults_md.css', function() { + return testFontFamily( + 'chrome://resources/css/text_defaults_md.css', + false /*expectingSystemFont*/); + }); +}); + +// Test that text_defaults_md.css reverts back to the text_defaults.css +// behavior when the WebUiSystemFont flag is enabled. +suite('TextDefaultsSystemFont', function() { + setup(function() { + document.body.innerHTML = window.trustedTypes!.emptyHTML; + }); + + test('text_defaults.css', function() { + return testFontFamily( + 'chrome://resources/css/text_defaults.css', + true /*expectingSystemFont*/); + }); + + test('text_defaults_md.css', function() { + return testFontFamily( + 'chrome://resources/css/text_defaults_md.css', + true /*expectingSystemFont*/); }); });
diff --git a/chrome/test/data/webui/css/tsconfig_base.json b/chrome/test/data/webui/css/tsconfig_base.json new file mode 100644 index 0000000..5b74298 --- /dev/null +++ b/chrome/test/data/webui/css/tsconfig_base.json
@@ -0,0 +1,8 @@ +{ + "extends": "../../../../../chrome/test/data/webui/tsconfig_base.json", + "compilerOptions": { + "types": [ + "chai", "mocha", "trusted-types", "w3c-css-typed-object-model-level-1" + ] + } +}
diff --git a/chrome/test/data/webui/polymer_browser_test_base.js b/chrome/test/data/webui/polymer_browser_test_base.js index e5f027f7..d19ae6d 100644 --- a/chrome/test/data/webui/polymer_browser_test_base.js +++ b/chrome/test/data/webui/polymer_browser_test_base.js
@@ -32,32 +32,8 @@ }; /** - * Imports the HTML file. - * @param {string} src The URL to load. - * @return {!Promise} A promise that is resolved/rejected on success/failure. - */ -PolymerTest.importHtml = function(src) { - var link = document.createElement('link'); - link.rel = 'import'; - var promise = new Promise(function(resolve, reject) { - link.onload = resolve; - link.onerror = reject; - }); - link.href = src; - document.head.appendChild(link); - return promise; -}; - -/** - * Removes all content from the body. In a vulcanized build, this retains the - * inlined tags so stylesheets and dom-modules are not discarded. + * Removes all content from the body. */ PolymerTest.clearBody = function() { - // Save the div where vulcanize inlines content before clearing the page. - var vulcanizeDiv = - document.querySelector('body > div[hidden][by-polymer-bundler]'); - document.body.innerHTML = ''; - if (vulcanizeDiv) { - document.body.appendChild(vulcanizeDiv); - } + document.body.innerHTML = window.trustedTypes.emptyHTML; };
diff --git a/chrome/test/data/webui/settings/chromeos/BUILD.gn b/chrome/test/data/webui/settings/chromeos/BUILD.gn index 95d05c5..05475e3 100644 --- a/chrome/test/data/webui/settings/chromeos/BUILD.gn +++ b/chrome/test/data/webui/settings/chromeos/BUILD.gn
@@ -124,7 +124,6 @@ "per_device_keyboard_remap_keys_test.js", "per_device_keyboard_test.js", "per_device_pointing_stick_subsection_test.js", - "per_device_touchpad_subsection_test.js", "personalization_page_with_personalization_hub_test.js", "privacy_hub_subpage_tests.js", "quick_unlock_authenticate_browsertest_chromeos.js", @@ -206,6 +205,7 @@ "device_page/per_device_mouse_subsection_test.ts", "device_page/per_device_mouse_test.ts", "device_page/per_device_pointing_stick_test.ts", + "device_page/per_device_touchpad_subsection_test.ts", "device_page/per_device_touchpad_test.ts", "internet_page/tether_connection_dialog_test.ts",
diff --git a/chrome/test/data/webui/settings/chromeos/device_page/per_device_touchpad_subsection_test.ts b/chrome/test/data/webui/settings/chromeos/device_page/per_device_touchpad_subsection_test.ts new file mode 100644 index 0000000..f1a17fb --- /dev/null +++ b/chrome/test/data/webui/settings/chromeos/device_page/per_device_touchpad_subsection_test.ts
@@ -0,0 +1,325 @@ +// 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. + +import {FakeInputDeviceSettingsProvider, fakeTouchpads, Router, routes, setInputDeviceSettingsProviderForTesting, SettingsPerDeviceTouchpadSubsectionElement, SettingsSliderElement, SettingsToggleButtonElement} from 'chrome://os-settings/chromeos/os_settings.js'; +import {CrToggleElement} from 'chrome://resources/cr_elements/cr_toggle/cr_toggle.js'; +import {assert} from 'chrome://resources/js/assert_ts.js'; +import {pressAndReleaseKeyOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js'; +import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js'; +import {flushTasks, waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js'; +import {isVisible} from 'chrome://webui-test/test_util.js'; + +const TOUCHPAD_SPEED_SETTING_ID = 405; + +suite('<settings-per-device-touchpad-subsection>', () => { + let subsection: SettingsPerDeviceTouchpadSubsectionElement; + let provider: FakeInputDeviceSettingsProvider; + + setup(async () => { + provider = new FakeInputDeviceSettingsProvider(); + provider.setFakeTouchpads(fakeTouchpads); + setInputDeviceSettingsProviderForTesting(provider); + subsection = + document.createElement('settings-per-device-touchpad-subsection'); + assert(subsection); + subsection.set('touchpad', {...fakeTouchpads[0]}); + subsection.set('allowScrollSettings_', true); + document.body.appendChild(subsection); + await flushTasks(); + }); + + teardown(() => { + subsection.remove(); + }); + + /** + * Test that API are updated when touchpad settings change. + */ + test('Update API when touchpad settings change', async () => { + const enableTapToClickButton = + subsection.shadowRoot!.querySelector<SettingsToggleButtonElement>( + '#enableTapToClick'); + assert(enableTapToClickButton); + enableTapToClickButton.click(); + await flushTasks(); + let updatedTouchpads = await provider.getConnectedTouchpadSettings(); + assertEquals( + updatedTouchpads[0]!.settings.tapToClickEnabled, + enableTapToClickButton.pref!.value); + + const enableTapDraggingButton = + subsection.shadowRoot!.querySelector<SettingsToggleButtonElement>( + '#enableTapDragging'); + assert(enableTapDraggingButton); + enableTapDraggingButton.click(); + await flushTasks(); + updatedTouchpads = await provider.getConnectedTouchpadSettings(); + assertEquals( + updatedTouchpads[0]!.settings.tapDraggingEnabled, + enableTapDraggingButton.pref!.value); + + const touchpadAccelerationButton = + subsection.shadowRoot!.querySelector<SettingsToggleButtonElement>( + '#touchpadAcceleration'); + assert(touchpadAccelerationButton); + touchpadAccelerationButton.click(); + await flushTasks(); + updatedTouchpads = await provider.getConnectedTouchpadSettings(); + assertEquals( + updatedTouchpads[0]!.settings.accelerationEnabled, + touchpadAccelerationButton.pref!.value); + + const touchpadScrollAccelerationButton = + subsection.shadowRoot!.querySelector<SettingsToggleButtonElement>( + '#touchpadScrollAcceleration'); + assert(touchpadScrollAccelerationButton); + touchpadScrollAccelerationButton.click(); + await flushTasks(); + updatedTouchpads = await provider.getConnectedTouchpadSettings(); + assertEquals( + updatedTouchpads[0]!.settings.scrollAcceleration, + touchpadScrollAccelerationButton.pref!.value); + + const touchpadScrollSpeedSlider = + subsection.shadowRoot!.querySelector<SettingsSliderElement>( + '#touchpadScrollSpeedSlider'); + assert(touchpadScrollSpeedSlider); + pressAndReleaseKeyOn( + touchpadScrollSpeedSlider.shadowRoot!.querySelector('cr-slider')!, 39, + [], 'ArrowRight'); + await flushTasks(); + updatedTouchpads = await provider.getConnectedTouchpadSettings(); + assertEquals( + updatedTouchpads[0]!.settings.scrollSensitivity, + touchpadScrollSpeedSlider.pref!.value); + + const touchpadSensitivitySlider = + subsection.shadowRoot!.querySelector<SettingsSliderElement>( + '#touchpadSensitivity'); + assert(touchpadSensitivitySlider); + pressAndReleaseKeyOn( + touchpadSensitivitySlider.shadowRoot!.querySelector('cr-slider')!, 39, + [], 'ArrowRight'); + await flushTasks(); + updatedTouchpads = await provider.getConnectedTouchpadSettings(); + assertEquals( + updatedTouchpads[0]!.settings.sensitivity, + touchpadSensitivitySlider.pref!.value); + + const touchpadHapticClickSensitivitySlider = + subsection.shadowRoot!.querySelector<SettingsSliderElement>( + '#touchpadHapticClickSensitivity'); + assert(touchpadHapticClickSensitivitySlider); + pressAndReleaseKeyOn( + touchpadHapticClickSensitivitySlider.shadowRoot!.querySelector( + 'cr-slider')!, + 39, [], 'ArrowRight'); + await flushTasks(); + updatedTouchpads = await provider.getConnectedTouchpadSettings(); + assertEquals( + updatedTouchpads[0]!.settings.hapticSensitivity, + touchpadHapticClickSensitivitySlider.pref!.value); + + const touchpadHapticFeedbackToggleButton = + subsection.shadowRoot!.querySelector<CrToggleElement>( + '#touchpadHapticFeedbackToggle'); + touchpadHapticFeedbackToggleButton!.click(); + await flushTasks(); + updatedTouchpads = await provider.getConnectedTouchpadSettings(); + assertEquals( + updatedTouchpads[0]!.settings.hapticEnabled, + touchpadHapticFeedbackToggleButton!.checked); + + const touchpadReverseScrollToggleButton = + subsection.shadowRoot!.querySelector<CrToggleElement>( + '#enableReverseScrollingToggle'); + touchpadReverseScrollToggleButton!.click(); + await flushTasks(); + updatedTouchpads = await provider.getConnectedTouchpadSettings(); + assertEquals( + updatedTouchpads[0]!.settings.reverseScrolling, + touchpadReverseScrollToggleButton!.checked); + }); + + /** + * Test that touchpad settings data are from the touchpad provider. + */ + test('Verify touchpad settings data', async () => { + let enableTapToClickButton = + subsection.shadowRoot!.querySelector<SettingsToggleButtonElement>( + '#enableTapToClick'); + assertEquals( + fakeTouchpads[0]!.settings.tapToClickEnabled, + enableTapToClickButton!.pref!.value); + let enableTapDraggingButton = + subsection.shadowRoot!.querySelector<SettingsToggleButtonElement>( + '#enableTapDragging'); + assertEquals( + fakeTouchpads[0]!.settings.tapDraggingEnabled, + enableTapDraggingButton!.pref!.value); + let touchpadAcceleration = + subsection.shadowRoot!.querySelector<SettingsToggleButtonElement>( + '#touchpadAcceleration'); + assertEquals( + fakeTouchpads[0]!.settings.accelerationEnabled, + touchpadAcceleration!.pref!.value); + let touchpadScrollAccelerationButton = + subsection.shadowRoot!.querySelector<SettingsToggleButtonElement>( + '#touchpadScrollAcceleration'); + assertTrue(isVisible(touchpadScrollAccelerationButton)); + assertEquals( + fakeTouchpads[0]!.settings.scrollAcceleration, + touchpadScrollAccelerationButton!.pref!.value); + let touchpadScrollSpeedSlider = + subsection.shadowRoot!.querySelector<SettingsSliderElement>( + '#touchpadScrollSpeedSlider'); + assertTrue(isVisible(touchpadScrollSpeedSlider)); + assertEquals( + fakeTouchpads[0]!.settings.scrollSensitivity, + touchpadScrollSpeedSlider!.pref!.value); + let touchpadSensitivitySlider = + subsection.shadowRoot!.querySelector<SettingsSliderElement>( + '#touchpadSensitivity'); + assertEquals( + fakeTouchpads[0]!.settings.sensitivity, + touchpadSensitivitySlider!.pref!.value); + let touchpadHapticClickSensitivitySlider = + subsection.shadowRoot!.querySelector<SettingsSliderElement>( + '#touchpadHapticClickSensitivity'); + assertTrue(isVisible(touchpadHapticClickSensitivitySlider)); + assertEquals( + fakeTouchpads[0]!.settings.hapticSensitivity, + touchpadHapticClickSensitivitySlider!.pref!.value); + let touchpadHapticFeedbackToggleButton = + subsection.shadowRoot!.querySelector<CrToggleElement>( + '#touchpadHapticFeedbackToggle'); + assertTrue(isVisible(touchpadHapticFeedbackToggleButton)); + assertEquals( + fakeTouchpads[0]!.settings.hapticEnabled, + touchpadHapticFeedbackToggleButton!.checked); + assertEquals( + fakeTouchpads[0]!.settings.reverseScrolling, + subsection.get('reverseScrollValue')); + + subsection.set('touchpad', fakeTouchpads[1]); + subsection.set('allowScrollSettings_', false); + + await flushTasks(); + enableTapToClickButton = + subsection.shadowRoot!.querySelector('#enableTapToClick'); + assertEquals( + fakeTouchpads[1]!.settings.tapToClickEnabled, + enableTapToClickButton!.pref!.value); + enableTapDraggingButton = + subsection.shadowRoot!.querySelector('#enableTapDragging'); + assertEquals( + fakeTouchpads[1]!.settings.tapDraggingEnabled, + enableTapDraggingButton!.pref!.value); + touchpadAcceleration = + subsection.shadowRoot!.querySelector('#touchpadAcceleration'); + assertEquals( + fakeTouchpads[1]!.settings.accelerationEnabled, + touchpadAcceleration!.pref!.value); + touchpadScrollAccelerationButton = + subsection.shadowRoot!.querySelector('#touchpadScrollAcceleration'); + assertFalse(isVisible(touchpadScrollAccelerationButton)); + touchpadScrollSpeedSlider = + subsection.shadowRoot!.querySelector('#touchpadScrollSpeedSlider'); + assertFalse(isVisible(touchpadScrollSpeedSlider)); + touchpadSensitivitySlider = + subsection.shadowRoot!.querySelector('#touchpadSensitivity'); + assertEquals( + fakeTouchpads[1]!.settings.sensitivity, + touchpadSensitivitySlider!.pref!.value); + touchpadHapticClickSensitivitySlider = + subsection.shadowRoot!.querySelector('#touchpadHapticClickSensitivity'); + assertFalse(isVisible(touchpadHapticClickSensitivitySlider)); + touchpadHapticFeedbackToggleButton = + subsection.shadowRoot!.querySelector('#touchpadHapticFeedbackToggle'); + assertFalse(isVisible(touchpadHapticFeedbackToggleButton)); + assertEquals( + fakeTouchpads[1]!.settings.reverseScrolling, + subsection.get('reverseScrollValue')); + }); + + /** + * Test haptic settings are correctly show or hidden based on the touchpad is + * haptic or not. + */ + test('Verify haptic settings visibility', async () => { + // Change the isHaptic state to true. + subsection.set('touchpad.isHaptic', true); + await flushTasks(); + // Verify haptic click sensitivity slider is visible in the page. + let hapticClickSensitivitySlider = + subsection.shadowRoot!.querySelector<SettingsSliderElement>( + '#touchpadHapticClickSensitivity'); + assertTrue(isVisible(hapticClickSensitivitySlider)); + + // Verify haptic feedback toggle button is visible in the page. + let hapticFeedbackToggleButton = + subsection.shadowRoot!.querySelector('#touchpadHapticFeedbackToggle'); + assertTrue(isVisible(hapticFeedbackToggleButton)); + + // Change the isHaptic state to false. + subsection.set('touchpad.isHaptic', false); + await flushTasks(); + // Verify haptic click sensitivity slider is not visible in the page. + hapticClickSensitivitySlider = + subsection.shadowRoot!.querySelector('#touchpadHapticClickSensitivity'); + assertFalse(isVisible(hapticClickSensitivitySlider)); + + // Verify haptic feedback toggle button is not visible in the page. + hapticFeedbackToggleButton = + subsection.shadowRoot!.querySelector('#touchpadHapticFeedbackToggle'); + assertFalse(isVisible(hapticFeedbackToggleButton)); + }); + + /** + * Verify entering the page with search tags matched will auto focus the + * searched element. + */ + test('Deep linking mixin focus on the first searched element', async () => { + const touchpadSensitivitySlider = + subsection.shadowRoot!.querySelector<SettingsSliderElement>( + '#touchpadSensitivity'); + subsection.set('touchpadIndex', 0); + // Enter the page from auto repeat search tag. + const url = new URLSearchParams( + 'search=touchpad+speed&settingId=' + + encodeURIComponent(TOUCHPAD_SPEED_SETTING_ID)); + + await Router.getInstance().navigateTo( + routes.PER_DEVICE_TOUCHPAD, + /* dynamicParams= */ url, /* removeSearch= */ true); + + assert(touchpadSensitivitySlider); + await waitAfterNextRender(touchpadSensitivitySlider); + assertEquals( + touchpadSensitivitySlider, subsection.shadowRoot!.activeElement); + }); + + /** + * Verify entering the page with search tags matched wll not auto focus the + * searched element if it's not the first keyboard displayed. + */ + test('Deep linkng mixin does not focus on second element', async () => { + const touchpadSensitivitySlider = + subsection.shadowRoot!.querySelector<SettingsSliderElement>( + '#touchpadSensitivity'); + subsection.set('touchpadIndex', 1); + // Enter the page from auto repeat search tag. + const url = new URLSearchParams( + 'search=touchpad+speed&settingId=' + + encodeURIComponent(TOUCHPAD_SPEED_SETTING_ID)); + + await Router.getInstance().navigateTo( + routes.PER_DEVICE_TOUCHPAD, + /* dynamicParams= */ url, /* removeSearch= */ true); + await flushTasks(); + + assert(touchpadSensitivitySlider); + assertEquals(null, subsection.shadowRoot!.activeElement); + }); +});
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js b/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js index 3342b79..ba427dc 100644 --- a/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js +++ b/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js
@@ -285,6 +285,11 @@ {enabled: ['ash::features::kInputDeviceSettingsSplit']}, ], [ + 'DevicePagePerDeviceTouchpadSubsection', + 'device_page/per_device_touchpad_subsection_test.js', + {enabled: ['ash::features::kInputDeviceSettingsSplit']} + ], + [ 'DisplayAndMagnificationPage', 'display_and_magnification_page_tests.js', ], @@ -489,10 +494,6 @@ {enabled: ['ash::features::kInputDeviceSettingsSplit']} ], [ - 'PerDeviceTouchpadSubsection', 'per_device_touchpad_subsection_test.js', - {enabled: ['ash::features::kInputDeviceSettingsSplit']} - ], - [ 'PersonalizationPageWithPersonalizationHub', 'personalization_page_with_personalization_hub_test.js', ],
diff --git a/chrome/test/data/webui/settings/chromeos/per_device_touchpad_subsection_test.js b/chrome/test/data/webui/settings/chromeos/per_device_touchpad_subsection_test.js deleted file mode 100644 index b6feef9..0000000 --- a/chrome/test/data/webui/settings/chromeos/per_device_touchpad_subsection_test.js +++ /dev/null
@@ -1,334 +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. - -import 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js'; - -import {FakeInputDeviceSettingsProvider, fakeTouchpads, Router, routes, setInputDeviceSettingsProviderForTesting, SettingsPerDeviceTouchpadSubsectionElement} from 'chrome://os-settings/chromeos/os_settings.js'; -import {assert} from 'chrome://resources/ash/common/assert.js'; -import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js'; -import {flushTasks, waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js'; -import {isVisible} from 'chrome://webui-test/test_util.js'; - -const TOUCHPAD_SPEED_SETTING_ID = 405; - -suite('PerDeviceTouchpadSubsection', function() { - /** - * @type {?SettingsPerDeviceTouchpadSubsectionElement} - */ - let subsection = null; - /** - * @type {?FakeInputDeviceSettingsProvider} - */ - let provider = null; - - - setup(() => { - PolymerTest.clearBody(); - }); - - teardown(() => { - subsection = null; - provider = null; - }); - - /** - * @return {!Promise} - */ - function initializePerDeviceTouchpadSubsection() { - provider = new FakeInputDeviceSettingsProvider(); - provider.setFakeTouchpads(fakeTouchpads); - setInputDeviceSettingsProviderForTesting(provider); - subsection = - document.createElement('settings-per-device-touchpad-subsection'); - assertTrue(subsection != null); - subsection.touchpad = {...fakeTouchpads[0]}; - subsection.allowScrollSettings_ = true; - document.body.appendChild(subsection); - return flushTasks(); - } - - /** - * @param {!Boolean} isHaptic - * @return {!Promise} - */ - function changeIsHapticState(isHaptic) { - subsection.touchpad = {...subsection.touchpad, isHaptic: isHaptic}; - return flushTasks(); - } - - /** - * @param {!Object} touchpad - * @param {!Boolean} allowScrollSettings - * @return {!Promise} - */ - function changeTouchpadSubsectionState(touchpad, allowScrollSettings) { - subsection.touchpad = touchpad; - subsection.allowScrollSettings_ = allowScrollSettings; - return flushTasks(); - } - - async function getConnectedTouchpadSettings() { - const touchpads = await provider.getConnectedTouchpadSettings(); - return touchpads; - } - - // Test that API are updated when touchpad settings change. - test('Update API when touchpad settings change', async () => { - await initializePerDeviceTouchpadSubsection(); - const enableTapToClickButton = - subsection.shadowRoot.querySelector('#enableTapToClick'); - enableTapToClickButton.click(); - await flushTasks(); - let updatedTouchpads = await getConnectedTouchpadSettings(); - assertEquals( - updatedTouchpads[0].settings.tapToClickEnabled, - enableTapToClickButton.pref.value); - - const enableTapDraggingButton = - subsection.shadowRoot.querySelector('#enableTapDragging'); - enableTapDraggingButton.click(); - await flushTasks(); - updatedTouchpads = await getConnectedTouchpadSettings(); - assertEquals( - updatedTouchpads[0].settings.tapDraggingEnabled, - enableTapDraggingButton.pref.value); - - const touchpadAccelerationButton = - subsection.shadowRoot.querySelector('#touchpadAcceleration'); - touchpadAccelerationButton.click(); - await flushTasks(); - updatedTouchpads = await getConnectedTouchpadSettings(); - assertEquals( - updatedTouchpads[0].settings.accelerationEnabled, - touchpadAccelerationButton.pref.value); - - const touchpadScrollAccelerationButton = - subsection.shadowRoot.querySelector('#touchpadScrollAcceleration'); - touchpadScrollAccelerationButton.click(); - await flushTasks(); - updatedTouchpads = await getConnectedTouchpadSettings(); - assertEquals( - updatedTouchpads[0].settings.scrollAcceleration, - touchpadScrollAccelerationButton.pref.value); - - const touchpadScrollSpeedSlider = assert( - subsection.shadowRoot.querySelector('#touchpadScrollSpeedSlider')); - MockInteractions.pressAndReleaseKeyOn( - touchpadScrollSpeedSlider.shadowRoot.querySelector('cr-slider'), - 39 /* right */, [], 'ArrowRight'); - await flushTasks(); - updatedTouchpads = await getConnectedTouchpadSettings(); - assertEquals( - updatedTouchpads[0].settings.scrollSensitivity, - touchpadScrollSpeedSlider.pref.value); - - const touchpadSensitivitySlider = - assert(subsection.shadowRoot.querySelector('#touchpadSensitivity')); - MockInteractions.pressAndReleaseKeyOn( - touchpadSensitivitySlider.shadowRoot.querySelector('cr-slider'), - 39 /* right */, [], 'ArrowRight'); - await flushTasks(); - updatedTouchpads = await getConnectedTouchpadSettings(); - assertEquals( - updatedTouchpads[0].settings.sensitivity, - touchpadSensitivitySlider.pref.value); - - const touchpadHapticClickSensitivitySlider = assert( - subsection.shadowRoot.querySelector('#touchpadHapticClickSensitivity')); - MockInteractions.pressAndReleaseKeyOn( - touchpadHapticClickSensitivitySlider.shadowRoot.querySelector( - 'cr-slider'), - 39 /* right */, [], 'ArrowRight'); - await flushTasks(); - updatedTouchpads = await getConnectedTouchpadSettings(); - assertEquals( - updatedTouchpads[0].settings.hapticSensitivity, - touchpadHapticClickSensitivitySlider.pref.value); - - const touchpadHapticFeedbackToggleButton = - subsection.shadowRoot.querySelector('#touchpadHapticFeedbackToggle'); - touchpadHapticFeedbackToggleButton.click(); - await flushTasks(); - updatedTouchpads = await getConnectedTouchpadSettings(); - assertEquals( - updatedTouchpads[0].settings.hapticEnabled, - touchpadHapticFeedbackToggleButton.checked); - - const touchpadReverseScrollToggleButton = - subsection.shadowRoot.querySelector('#enableReverseScrollingToggle'); - touchpadReverseScrollToggleButton.click(); - await flushTasks(); - updatedTouchpads = await getConnectedTouchpadSettings(); - assertEquals( - updatedTouchpads[0].settings.reverseScrolling, - touchpadReverseScrollToggleButton.checked); - }); - - // Test that touchpad settings data are from the touchpad provider. - test('Verify touchpad settings data', async () => { - await initializePerDeviceTouchpadSubsection(); - let enableTapToClickButton = - subsection.shadowRoot.querySelector('#enableTapToClick'); - assertEquals( - fakeTouchpads[0].settings.tapToClickEnabled, - enableTapToClickButton.pref.value); - let enableTapDraggingButton = - subsection.shadowRoot.querySelector('#enableTapDragging'); - assertEquals( - fakeTouchpads[0].settings.tapDraggingEnabled, - enableTapDraggingButton.pref.value); - let touchpadAcceleration = - subsection.shadowRoot.querySelector('#touchpadAcceleration'); - assertEquals( - fakeTouchpads[0].settings.accelerationEnabled, - touchpadAcceleration.pref.value); - let touchpadScrollAccelerationButton = - subsection.shadowRoot.querySelector('#touchpadScrollAcceleration'); - assertTrue(isVisible(touchpadScrollAccelerationButton)); - assertEquals( - fakeTouchpads[0].settings.scrollAcceleration, - touchpadScrollAccelerationButton.pref.value); - let touchpadScrollSpeedSlider = assert( - subsection.shadowRoot.querySelector('#touchpadScrollSpeedSlider')); - assertTrue(isVisible(touchpadScrollSpeedSlider)); - assertEquals( - fakeTouchpads[0].settings.scrollSensitivity, - touchpadScrollSpeedSlider.pref.value); - let touchpadSensitivitySlider = - assert(subsection.shadowRoot.querySelector('#touchpadSensitivity')); - assertEquals( - fakeTouchpads[0].settings.sensitivity, - touchpadSensitivitySlider.pref.value); - let touchpadHapticClickSensitivitySlider = assert( - subsection.shadowRoot.querySelector('#touchpadHapticClickSensitivity')); - assertTrue(isVisible(touchpadHapticClickSensitivitySlider)); - assertEquals( - fakeTouchpads[0].settings.hapticSensitivity, - touchpadHapticClickSensitivitySlider.pref.value); - let touchpadHapticFeedbackToggleButton = - subsection.shadowRoot.querySelector('#touchpadHapticFeedbackToggle'); - assertTrue(isVisible(touchpadHapticFeedbackToggleButton)); - assertEquals( - fakeTouchpads[0].settings.hapticEnabled, - touchpadHapticFeedbackToggleButton.checked); - assertEquals( - fakeTouchpads[0].settings.reverseScrolling, - subsection.reverseScrollValue); - - await changeTouchpadSubsectionState(fakeTouchpads[1], false); - enableTapToClickButton = - subsection.shadowRoot.querySelector('#enableTapToClick'); - assertEquals( - fakeTouchpads[1].settings.tapToClickEnabled, - enableTapToClickButton.pref.value); - enableTapDraggingButton = - subsection.shadowRoot.querySelector('#enableTapDragging'); - assertEquals( - fakeTouchpads[1].settings.tapDraggingEnabled, - enableTapDraggingButton.pref.value); - touchpadAcceleration = - subsection.shadowRoot.querySelector('#touchpadAcceleration'); - assertEquals( - fakeTouchpads[1].settings.accelerationEnabled, - touchpadAcceleration.pref.value); - touchpadScrollAccelerationButton = - subsection.shadowRoot.querySelector('#touchpadScrollAcceleration'); - assertFalse(isVisible(touchpadScrollAccelerationButton)); - touchpadScrollSpeedSlider = - subsection.shadowRoot.querySelector('#touchpadScrollSpeedSlider'); - assertFalse(isVisible(touchpadScrollSpeedSlider)); - touchpadSensitivitySlider = - assert(subsection.shadowRoot.querySelector('#touchpadSensitivity')); - assertEquals( - fakeTouchpads[1].settings.sensitivity, - touchpadSensitivitySlider.pref.value); - touchpadHapticClickSensitivitySlider = - subsection.shadowRoot.querySelector('#touchpadHapticClickSensitivity'); - assertFalse(isVisible(touchpadHapticClickSensitivitySlider)); - touchpadHapticFeedbackToggleButton = - subsection.shadowRoot.querySelector('#touchpadHapticFeedbackToggle'); - assertFalse(isVisible(touchpadHapticFeedbackToggleButton)); - assertEquals( - fakeTouchpads[1].settings.reverseScrolling, - subsection.reverseScrollValue); - }); - - /** - * Test haptic settings are correctly show or hidden based on the touchpad is - * haptic or not. - */ - test('Verify haptic settings visbility', async () => { - await initializePerDeviceTouchpadSubsection(); - // Change the isHaptic state to true. - await changeIsHapticState(true); - // Verify haptic click sensitivity slider is visible in the page. - let hapticClickSensitivitySlider = - subsection.shadowRoot.querySelector('#touchpadHapticClickSensitivity'); - assertTrue(isVisible(hapticClickSensitivitySlider)); - - // Verify haptic feedback toggle button is visible in the page. - let hapticFeedbackToggleButton = - subsection.shadowRoot.querySelector('#touchpadHapticFeedbackToggle'); - assertTrue(isVisible(hapticFeedbackToggleButton)); - - // Change the isHaptic state to false. - await changeIsHapticState(false); - // Verify haptic click sensitivity slider is not visible in the page. - hapticClickSensitivitySlider = - subsection.shadowRoot.querySelector('#touchpadHapticClickSensitivity'); - assertFalse(isVisible(hapticClickSensitivitySlider)); - - // Verify haptic feedback toggle button is not visible in the page. - hapticFeedbackToggleButton = - subsection.shadowRoot.querySelector('#touchpadHapticFeedbackToggle'); - assertFalse(isVisible(hapticFeedbackToggleButton)); - }); - - /** - * Verify entering the page with search tags matched will auto focus the - * searched element. - */ - test('deep linking mixin focus on the first searched element', async () => { - await initializePerDeviceTouchpadSubsection(); - const touchpadSensitivitySlider = - subsection.shadowRoot.querySelector('#touchpadSensitivity'); - subsection.touchpadIndex = 0; - // Enter the page from auto repeat search tag. - const url = new URLSearchParams( - 'search=touchpad+speed&settingId=' + - encodeURIComponent(TOUCHPAD_SPEED_SETTING_ID)); - - await Router.getInstance().navigateTo( - routes.PER_DEVICE_TOUCHPAD, - /* dynamicParams= */ url, /* removeSearch= */ true); - - await waitAfterNextRender(touchpadSensitivitySlider); - assertTrue(!!touchpadSensitivitySlider); - assertEquals( - subsection.shadowRoot.activeElement, touchpadSensitivitySlider); - }); - - /** - * Verify entering the page with search tags matched wll not auto focus the - * searched element if it's not the first keyboard displayed. - */ - test('deep linkng mixin does not focus on second element', async () => { - await initializePerDeviceTouchpadSubsection(); - const touchpadSensitivitySlider = - subsection.shadowRoot.querySelector('#touchpadSensitivity'); - subsection.touchpadIndex = 1; - // Enter the page from auto repeat search tag. - const url = new URLSearchParams( - 'search=touchpad+speed&settingId=' + - encodeURIComponent(TOUCHPAD_SPEED_SETTING_ID)); - - await Router.getInstance().navigateTo( - routes.PER_DEVICE_TOUCHPAD, - /* dynamicParams= */ url, /* removeSearch= */ true); - await flushTasks(); - - assertTrue(!!touchpadSensitivitySlider); - assertFalse(!!subsection.shadowRoot.activeElement); - }); -});
diff --git a/chromeos/ash/components/dbus/BUILD.gn b/chromeos/ash/components/dbus/BUILD.gn index 799d131..4119288 100644 --- a/chromeos/ash/components/dbus/BUILD.gn +++ b/chromeos/ash/components/dbus/BUILD.gn
@@ -96,6 +96,16 @@ ] } +# TODO(b/273117306): The below protos generate to +# gen/chromeos/ash/components/dbus/* which is the equivalent of +# *this build file*. This forces us to redefine proto_out_dir every +# time and jump through hoops for importing protos. To minimise +# configuration a better approach uses the default gen location +# gen/third_party/cros_system_api/dbus/* and avoids the below cfg. +config("system_api_proto_include_cfg") { + include_dirs = [ "${root_gen_dir}/chromeos/ash/components/dbus" ] +} + proto_library("metrics_event_proto") { sources = [ "//third_party/cros_system_api/dbus/metrics_event/metrics_event.proto" ] @@ -135,6 +145,14 @@ proto_out_dir = "chromeos/ash/components/dbus/vm_sk_forwarding" } +proto_library("vm_wl_proto") { + sources = [ "//third_party/cros_system_api/dbus/vm_wl/wl.proto" ] + proto_out_dir = "chromeos/ash/components/dbus/vm_wl" + proto_deps = [ ":vm_applications_apps_proto" ] + import_dirs = [ "//third_party/cros_system_api/dbus" ] + extra_configs = [ ":system_api_proto_include_cfg" ] +} + proto_library("vm_permission_service_proto") { sources = [ "//third_party/cros_system_api/dbus/vm_permission_service/vm_permission_service.proto" ]
diff --git a/chromeos/ash/components/dbus/featured/fake_featured_client.cc b/chromeos/ash/components/dbus/featured/fake_featured_client.cc index 73e233ca..bc88d348 100644 --- a/chromeos/ash/components/dbus/featured/fake_featured_client.cc +++ b/chromeos/ash/components/dbus/featured/fake_featured_client.cc
@@ -4,9 +4,8 @@ #include "chromeos/ash/components/dbus/featured/fake_featured_client.h" -#include <string> - #include "base/check_op.h" +#include "base/containers/queue.h" #include "base/functional/callback.h" #include "chromeos/ash/components/dbus/featured/featured.pb.h" #include "dbus/object_proxy.h" @@ -35,14 +34,25 @@ return g_instance; } -void FakeFeaturedClient::SetCallbackSuccess(bool success) { - success_ = success; -} - void FakeFeaturedClient::HandleSeedFetched( const ::featured::SeedDetails& safe_seed, base::OnceCallback<void(bool success)> callback) { - std::move(callback).Run(success_); + handle_seed_fetched_attempts_++; + + if (responses_.empty()) { + LOG(ERROR) << "Insufficient amount of responses added. Call AddResponse to " + "add expected response to invoke the callback with"; + std::move(callback).Run(false); + return; + } + + bool success = responses_.front(); + responses_.pop(); + std::move(callback).Run(success); +} + +void FakeFeaturedClient::AddResponse(bool success) { + responses_.push(success); } } // namespace ash::featured
diff --git a/chromeos/ash/components/dbus/featured/fake_featured_client.h b/chromeos/ash/components/dbus/featured/fake_featured_client.h index 2f204470..a81ceaf 100644 --- a/chromeos/ash/components/dbus/featured/fake_featured_client.h +++ b/chromeos/ash/components/dbus/featured/fake_featured_client.h
@@ -6,7 +6,9 @@ #define CHROMEOS_ASH_COMPONENTS_DBUS_FEATURED_FAKE_FEATURED_CLIENT_H_ #include "base/component_export.h" +#include "base/containers/queue.h" #include "base/functional/callback.h" +#include "base/logging.h" #include "chromeos/ash/components/dbus/featured/featured.pb.h" #include "chromeos/ash/components/dbus/featured/featured_client.h" @@ -17,25 +19,35 @@ : public FeaturedClient { public: FakeFeaturedClient(); + FakeFeaturedClient(const FakeFeaturedClient&) = delete; FakeFeaturedClient& operator=(const FakeFeaturedClient&) = delete; + ~FakeFeaturedClient() override; // Returns the global FakeFeaturedClient instance. Returns `nullptr` if // it is not initialized. static FakeFeaturedClient* Get(); - // Sets the callback parameter for `HandleSeedFetched`. - void SetCallbackSuccess(bool success); - - // |callback| is run with true by default. Call |SetCallbackSuccess| - // to change the callback parameter. + // |callback| is run with the first response in `responses_`. Call + // `AddResponse` to add a response. |callback| is invoked with false if not + // enough responses are provided. void HandleSeedFetched( const ::featured::SeedDetails& safe_seed, base::OnceCallback<void(bool success)> callback) override; + // Adds a response to call `HandleSeedFetched` with. + void AddResponse(bool success); + + // Returns the number of times `HandleSeedFetched` was called. Used for + // testing. + int handle_seed_fetched_attempts() const { + return handle_seed_fetched_attempts_; + } + private: - bool success_ = true; + base::queue<bool> responses_; + size_t handle_seed_fetched_attempts_ = 0; }; } // namespace ash::featured
diff --git a/chromeos/ash/components/dbus/featured/featured_client_unittest.cc b/chromeos/ash/components/dbus/featured/featured_client_unittest.cc index 783e74b..497bb49 100644 --- a/chromeos/ash/components/dbus/featured/featured_client_unittest.cc +++ b/chromeos/ash/components/dbus/featured/featured_client_unittest.cc
@@ -108,8 +108,8 @@ EXPECT_EQ(FeaturedClient::Get(), nullptr); } -// Check that HandleSeedFetched runs the callback with a false success value if -// the server (platform) returns an error responses. +// Check that `HandleSeedFetched` runs the callback with a false success value +// if the server (platform) returns an error responses. TEST_F(FeaturedClientTest, HandleSeedFetched_Failure_ErrorResponse) { EXPECT_CALL(*proxy_, DoCallMethod(_, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, _)) @@ -144,8 +144,8 @@ EXPECT_EQ(FeaturedClient::Get(), nullptr); } -// Check that HandleSeedFetched runs the callback with a false success value if -// the method call is unsuccessful (response is a nullptr). +// Check that `HandleSeedFetched` runs the callback with a false success value +// if the method call is unsuccessful (response is a nullptr). TEST_F(FeaturedClientTest, HandleSeedFetched_Failure_NullResponse) { EXPECT_CALL(*proxy_, DoCallMethod(_, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, _)) @@ -175,9 +175,9 @@ EXPECT_EQ(FeaturedClient::Get(), nullptr); } -// Check that Fake runs HandleSeedFetched callback with a true success value by -// default. -TEST_F(FeaturedClientTest, FakeHandleSeedFetched_Success) { +// Check that Fake runs `HandleSeedFetched` callback with a false success value +// by default (no expected responses added). +TEST_F(FeaturedClientTest, FakeHandleSeedFetched_InvokeFalseByDefault) { FeaturedClient::InitializeFake(); FakeFeaturedClient* client = FakeFeaturedClient::Get(); @@ -188,36 +188,38 @@ bool ran_callback = false; client->HandleSeedFetched( safe_seed, base::BindLambdaForTesting([&ran_callback](bool success) { - EXPECT_TRUE(success); - ran_callback = true; - })); - // Ensures the callback was executed. - EXPECT_TRUE(ran_callback); - - FeaturedClient::Shutdown(); - - EXPECT_EQ(FakeFeaturedClient::Get(), nullptr); -} - -// Check that Fake runs HandleSeedFetched callback with a false success value -// when set. -TEST_F(FeaturedClientTest, FakeHandleSeedFetched_Failure) { - FeaturedClient::InitializeFake(); - FakeFeaturedClient* client = FakeFeaturedClient::Get(); - - ASSERT_NE(client, nullptr); - - ::featured::SeedDetails safe_seed; - client->SetCallbackSuccess(false); - - bool ran_callback = false; - client->HandleSeedFetched( - safe_seed, base::BindLambdaForTesting([&ran_callback](bool success) { EXPECT_FALSE(success); ran_callback = true; })); // Ensures the callback was executed. EXPECT_TRUE(ran_callback); + EXPECT_EQ(client->handle_seed_fetched_attempts(), 1); + + FeaturedClient::Shutdown(); + + EXPECT_EQ(FakeFeaturedClient::Get(), nullptr); +} + +// Check that Fake runs `HandleSeedFetched` callback with value added by +// `AddResponse`. +TEST_F(FeaturedClientTest, FakeHandleSeedFetched_InvokeSuccessWhenSet) { + FeaturedClient::InitializeFake(); + FakeFeaturedClient* client = FakeFeaturedClient::Get(); + + ASSERT_NE(client, nullptr); + + ::featured::SeedDetails safe_seed; + client->AddResponse(true); + + bool ran_callback = false; + client->HandleSeedFetched( + safe_seed, base::BindLambdaForTesting([&ran_callback](bool success) { + EXPECT_TRUE(success); + ran_callback = true; + })); + // Ensures the callback was executed. + EXPECT_TRUE(ran_callback); + EXPECT_EQ(client->handle_seed_fetched_attempts(), 1); FeaturedClient::Shutdown();
diff --git a/chromeos/ash/components/dbus/shill/README.md b/chromeos/ash/components/dbus/shill/README.md index f9db152..bb0ddf98 100644 --- a/chromeos/ash/components/dbus/shill/README.md +++ b/chromeos/ash/components/dbus/shill/README.md
@@ -4,68 +4,64 @@ Shill via D-Bus. These clients provide a high-level API that enables interaction with the following Shill services: * [Shill manager service](#shill-manager-client) -* Shill device service -* [Shill IPconfig service](#shill-ipconfig-client) -* Shill profile service -* Shill service service +* [Shill device service](#shill-device-client) +* [Shill IPConfig service](#shill-ipconfig-client) +* [Shill profile service](#shill-profile-client) +* [Shill service service](#shill-service-client) -## Shill clients initialization and shutdown +All of the clients used to interact with these Shill services are instantiated +as singletons, and all usage of their interfaces must happen on the same thread +that was used to instantiate the client. + +For more information about Shill, the connection manager for ChromeOS, see the +[official +README.md](https://source.chromium.org/chromiumos/chromiumos/codesearch/+/main:src/platform2/shill/README.md). + +# Service clients + ChromeOS interacts with Shill services through their corresponding Shill D-Bus clients. To initialize all Shill D-Bus clients, the [`shill_clients::Initialize()`](https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ash/dbus/ash_dbus_helper.cc;l=136;drc=1e745d6190686a85eea668b86350080be45b55f9) function is used. This function is called from the [`ash::InitializeDBus()`](https://source.chromium.org/chromium/chromium/src/+/main:chrome/app/chrome_main_delegate.cc;l=650;drc=705fc04a0d21cfe6709b8a56750704a19290ce96) -function during the Ash/Chrome UI -[early initialization](https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ash/dbus/ash_dbus_helper.cc;l=123;drc=33ab5f99dea200ad10ab79d898465e82f8a4ae77) -stage as there are many other components that depend on Shill clients. -The `shill_clients::Initialize()` function sets up a single global instance of -each Shill D-Bus client which are accessible by public static functions provided -by each client individually, e.g.: `ShillManagerClient::Get()`.\ -Additionally, it also provides a +function during the Ash/Chrome UI [early +initialization](https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ash/dbus/ash_dbus_helper.cc;l=123;drc=33ab5f99dea200ad10ab79d898465e82f8a4ae77) +stage as there are many other components that depend on Shill clients. The +`shill_clients::Initialize()` function sets up a single global instance of each +Shill D-Bus client which are accessible by public static functions provided by +each client individually, e.g. [`ShillManagerClient::Get()`](https://source.chromium.org/chromium/chromium/src/+/refs/heads/main:chromeos/ash/components/dbus/shill/shill_manager_client.h;l=160;drc=2450f2f5d0ce0da9b8cf493c533f9528ff17bab6). Similarly, +the [`shill_clients::InitializeFakes()`](https://source.chromium.org/chromium/chromium/src/+/main:chromeos/ash/components/dbus/shill/shill_clients.cc;l=34;drc=e4714ce987b39d3207473e0cd5cc77fbbbf37fda) -function which initializes the global singleton instance with fake implementation. -This function is mostly used for unit tests or ChromeOS-Linux build.\ -Then, during Ash/Chrome UI shutdown, the +function is also provided which initializes the global singleton instance with fake +implementations. This function is mostly used for unit tests or ChromeOS-Linux +build. Finally, during Ash/Chrome UI shutdown, the [`ash::ShutdownDBus()`](https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ash/dbus/ash_dbus_helper.cc;l=256;drc=1e745d6190686a85eea668b86350080be45b55f9) function is called, and the [`shill_clients::Shutdown()`](https://source.chromium.org/chromium/chromium/src/+/main:chromeos/ash/components/dbus/shill/shill_clients.cc;l=47;drc=e4714ce987b39d3207473e0cd5cc77fbbbf37fda) function is used to bring down all the Shill D-Bus clients. - -## Shill manager client -The [ShillManagerClient](https://source.chromium.org/chromium/chromium/src/+/main:chromeos/ash/components/dbus/shill/shill_manager_client.h;drc=f10ad519eaa48f765938cc453b97f3333f1d1a9d) -class provides an interface for interacting with the Shill manager service +## Shill manager client {#shill-manager-client} +The [`ShillManagerClient`](https://source.chromium.org/chromium/chromium/src/+/main:chromeos/ash/components/dbus/shill/shill_manager_client.h;drc=f10ad519eaa48f765938cc453b97f3333f1d1a9d) +class provides the interface for interacting with the Shill manager service interface which is the top-level singleton exposed by Shill that provides global system properties. It enables Chrome to: -* Get or set shill manager properties +* Get or set Shill manager properties * Enable or disable technologies such as Wi-Fi/cellular networks * Scan and connect to the best service, like searching for nearby Wi-Fi networks * Configure networks, such as setting up a Wi-Fi network with a password * Add or remove passpoint credentials * Tethering related functionality such as enable or disable hotspot tethering -For detailed documentation on the Shill Manager DBus API, please refer to +For detailed documentation on the Shill Manager D-Bus API, please refer to [manager-api.txt](https://source.chromium.org/chromiumos/chromiumos/codesearch/+/main:src/platform2/shill/doc/manager-api.txt;drc=89f1629aac064713d70908436fa9834c4f443551). -All Shill methods must be called from the origin thread that initializes the -DBusThreadManager instance. Most methods that make Shill Manager calls pass -two callbacks: -* callback: invoked if the method call succeeds -* error_callback: invoked if the method call fails or returns an error response +## Shill device client {#shill-device-client} -Additionally, ShillManagerClient provides a -[test interface](https://source.chromium.org/chromium/chromium/src/+/main:chromeos/ash/components/dbus/shill/shill_manager_client.h;l=51;drc=f10ad519eaa48f765938cc453b97f3333f1d1a9d) -that allows you to set up fake devices, services, and technologies for ChromeOS -unit testing purposes. This interface is implemented in the -[FakeShillManagerClient](https://source.chromium.org/chromium/chromium/src/+/main:chromeos/ash/components/dbus/shill/fake_shill_manager_client.h;drc=f10ad519eaa48f765938cc453b97f3333f1d1a9d) -class, which simulates the behavior of ShillManagerClient and provides APIs for -setting fake responses. For example, `SetInteractiveDelay` sets the fake -interactive delay for the following shill response for testing purposes, while -`SetSimulateConfigurationResult` sets the following `ConfigureService` call to -succeed, fail or timeout. +TODO: Discuss the Shill device client. -## Shill IPConfig client -The [ShillIPConfigClient](https://source.chromium.org/chromium/chromium/src/+/main:chromeos/ash/components/dbus/shill/shill_ipconfig_client.h;drc=ad947e92bd398452f42173e7a39ed7ab2e4ad094) +## Shill IPConfig client {#shill-ipconfig-client} + +The [`ShillIPConfigClient`](https://source.chromium.org/chromium/chromium/src/+/main:chromeos/ash/components/dbus/shill/shill_ipconfig_client.h;drc=ad947e92bd398452f42173e7a39ed7ab2e4ad094) class provides an interface for interacting with the Shill IPConfig interface which is the top-level singleton exposed by Shill that provides Layer 3 configuration. It enables Chrome to: @@ -75,11 +71,82 @@ For detailed documentation on the Shill IPConfig DBus API, please refer to [ipconfig-api.txt](https://source.chromium.org/chromiumos/chromiumos/codesearch/+/main:src/platform2/shill/doc/ipconfig-api.txt;drc=9a98a2fb4b28a8e3c32d7eafb39395ccbc730538). -All Shill methods must be called from the origin thread that initializes the -DBusThreadManager instance. Most methods that make Shill IPConfig calls pass -a callback that is invoked when the method finishes. +## Shill profile client {#shill-profile-client} -Additionally, ShillIPConfigClient provides a -[test interface](https://source.chromium.org/chromium/chromium/src/+/main:chromeos/ash/components/dbus/shill/shill_ipconfig_client.h;l=32-40;drc=ad947e92bd398452f42173e7a39ed7ab2e4ad094) -that allows you to add fake IPConfig entries for ChromeOS unit testing -purposes. This interface is implemented in the [FakeShillIPConfigClient](https://source.chromium.org/chromium/chromium/src/+/refs/heads/main:chromeos/ash/components/dbus/shill/fake_shill_ipconfig_client.h;drc=ad947e92bd398452f42173e7a39ed7ab2e4ad094). \ No newline at end of file +TODO: Discuss the Shill profile client. + +## Shill service client {#shill-service-client} +The +[`ShillServiceClient`](https://source.chromium.org/chromium/chromium/src/+/main:chromeos/ash/components/dbus/shill/shill_service_client.h;drc=af33e6b506bcb54e29efd850e2eb546f476ee63a) +class provides the interface for interacting with the Shill service service. +Services are how Shill represents network configurations and connections through +an associated device within the Platform layer and can be thought of the source +of truth for these network configurations +and connections on ChromeOS devices. While Chrome, i.e. the Software layer, +generally relies on the read-only +[`NetworkState`](https://source.chromium.org/chromium/chromium/src/+/main:chromeos/ash/components/network/README.md;l=100-149;drc=4a50d13fc73268ef4a27cf67dc1eff40ea6f997a) +class for network properties, the `NetworkState` class is effectively just a +cache of a Shill service. + +The `ShillServiceClient` class provides a thorough interface for interacting +with Shill services: +* Get, set, and clear Shill service properties +* Connect, disconnect, and remove Shill services +* Listen for changes to Shill service properties +* Retrieve the passphrase associated with the Shill service +* Miscellaneous behavior e.g. complete cellular activation + +For detailed documentation on the Shill Service D-Bus API, please refer to +[service-api.txt](https://source.chromium.org/chromiumos/chromiumos/codesearch/+/main:src/platform2/shill/doc/service-api.txt). + +# Testing interfaces + +Each of the clients discussed provides a testing interface that can be used to +effectively configure Shill to be in a specific, custom state during testing. +These testing interfaces are declared within the corresponding client class and +can be used within tests in multiple ways. For example, the +[`NetworkTestHelperBase`](https://source.chromium.org/chromium/chromium/src/+/main:chromeos/ash/components/network/network_test_helper_base.h;drc=e4714ce987b39d3207473e0cd5cc77fbbbf37fda) +class can be used within tests which will initialize the entire network stack, +including the test interfaces for each of the clients. + +## Shill manager client {#shill-manager-client-testing} + +The +[`ShillManagerClient::TestInterface`](https://source.chromium.org/chromium/chromium/src/+/main:chromeos/ash/components/dbus/shill/shill_manager_client.h;l=51;drc=f10ad519eaa48f765938cc453b97f3333f1d1a9d) +class supports a wide variety of functionality that is very useful when testing: +* Creating fakes, e.g. devices, services, technologies and more +* Configuring the delay before success/failure callbacks should be invoked +* Configuring whether certain function calls should succeed, fail, or timeout + +The `ShillManagerClient::TestInterface` interface is implemented by +[`FakeShillManagerClient`](https://source.chromium.org/chromium/chromium/src/+/main:chromeos/ash/components/dbus/shill/fake_shill_manager_client.h;drc=f10ad519eaa48f765938cc453b97f3333f1d1a9d). + +## Shill device client {#shill-device-client-testing} + +TODO: Discuss the Shill device client testing interface. + +## Shill IPConfig client {#shill-ipconfig-client-testing} + +The +[`ShillIPConfigClient::TestInterface`](https://source.chromium.org/chromium/chromium/src/+/main:chromeos/ash/components/dbus/shill/shill_ipconfig_client.h;l=32-40;drc=ad947e92bd398452f42173e7a39ed7ab2e4ad094) +class supports adding fake IPConfig entries during testing. This interface is +implemented by +[`FakeShillIPConfigClient`](https://source.chromium.org/chromium/chromium/src/+/refs/heads/main:chromeos/ash/components/dbus/shill/fake_shill_ipconfig_client.h;drc=ad947e92bd398452f42173e7a39ed7ab2e4ad094). + +## Shill profile client {#shill-profile-client-testing} + +TODO: Discuss the Shill profile client testing interface. + +## Shill service client {#shill-service-client-testing} + +The +[`ShillServiceClient::TestInterface`](https://source.chromium.org/chromium/chromium/src/+/main:chromeos/ash/components/dbus/shill/shill_service_client.h;drc=af33e6b506bcb54e29efd850e2eb546f476ee63a) +class provides a number of different APIs for interacting with services: +* Configuring fake services +* Getting, setting, and clearing service properties +* Configuring whether certain function calls should succeed, fail, or timeout +* Configuring the connect behavior of services, e.g. custom behavior on + connection + +The `ShillServiceClient::TestInterface` interface is implemented by +[`FakeShillServiceClient`](https://source.chromium.org/chromium/chromium/src/+/main:chromeos/ash/components/dbus/shill/fake_shill_service_client.h;drc=af33e6b506bcb54e29efd850e2eb546f476ee63a).
diff --git a/chromeos/ash/components/nearby/common/DIR_METADATA b/chromeos/ash/components/nearby/common/DIR_METADATA new file mode 100644 index 0000000..d6d7997 --- /dev/null +++ b/chromeos/ash/components/nearby/common/DIR_METADATA
@@ -0,0 +1,4 @@ +buganizer { + component_id: 1131838 +} +team_email: "chromeos-cross-device-eng@google.com "
diff --git a/chromeos/ash/components/nearby/common/OWNERS b/chromeos/ash/components/nearby/common/OWNERS new file mode 100644 index 0000000..1064f36b --- /dev/null +++ b/chromeos/ash/components/nearby/common/OWNERS
@@ -0,0 +1,3 @@ +file://chrome/browser/nearby_sharing/OWNERS +hansberry@chromium.org +julietlevesque@google.com
diff --git a/components/exo/wayland/BUILD.gn b/components/exo/wayland/BUILD.gn index fbcfad36..c1ad58c 100644 --- a/components/exo/wayland/BUILD.gn +++ b/components/exo/wayland/BUILD.gn
@@ -132,6 +132,8 @@ "zxdg_decoration_manager.h", "zxdg_output_manager.cc", "zxdg_output_manager.h", + "zxdg_shell.cc", + "zxdg_shell.h", ] defines = [ "EXO_IMPLEMENTATION" ]
diff --git a/components/exo/wayland/clients/client_base.cc b/components/exo/wayland/clients/client_base.cc index 7a58b2cd..695c09e 100644 --- a/components/exo/wayland/clients/client_base.cc +++ b/components/exo/wayland/clients/client_base.cc
@@ -619,29 +619,65 @@ wl_shell_surface_set_toplevel(shell_surface.get()); } } else { - xdg_surface_.reset(xdg_wm_base_get_xdg_surface(globals_.xdg_wm_base.get(), - surface_.get())); - if (!xdg_surface_) { - LOG(ERROR) << "Can't get xdg surface"; - return false; + if (!globals_.xdg_wm_base) { + // use zxdg + if (!globals_.xdg_shell_v6) { + LOG(ERROR) << "Can't find xdg_shell or zxdg_shell_v6 interface"; + return false; + } + + zxdg_surface_.reset(zxdg_shell_v6_get_xdg_surface( + globals_.xdg_shell_v6.get(), surface_.get())); + if (!zxdg_surface_) { + LOG(ERROR) << "Can't get zxdg surface"; + return false; + } + static const zxdg_surface_v6_listener zxdg_surface_v6_listener = { + [](void* data, struct zxdg_surface_v6* zxdg_surface_v6, + uint32_t layout_mode) { + zxdg_surface_v6_ack_configure(zxdg_surface_v6, layout_mode); + }, + }; + zxdg_surface_v6_add_listener(zxdg_surface_.get(), + &zxdg_surface_v6_listener, this); + zxdg_toplevel_.reset(zxdg_surface_v6_get_toplevel(zxdg_surface_.get())); + if (!zxdg_toplevel_) { + LOG(ERROR) << "Can't get zxdg toplevel"; + return false; + } + static const zxdg_toplevel_v6_listener zxdg_toplevel_v6_listener = { + [](void* data, struct zxdg_toplevel_v6* zxdg_toplevel_v6, + int32_t width, int32_t height, struct wl_array* states) {}, + [](void* data, struct zxdg_toplevel_v6* zxdg_toplevel_v6) {}}; + zxdg_toplevel_v6_add_listener(zxdg_toplevel_.get(), + &zxdg_toplevel_v6_listener, this); + } else { + // use xdg + xdg_surface_.reset(xdg_wm_base_get_xdg_surface(globals_.xdg_wm_base.get(), + surface_.get())); + if (!xdg_surface_) { + LOG(ERROR) << "Can't get xdg surface"; + return false; + } + static const xdg_surface_listener xdg_surface_listener = { + [](void* data, struct xdg_surface* xdg_surface, + uint32_t layout_mode) { + xdg_surface_ack_configure(xdg_surface, layout_mode); + }, + }; + xdg_surface_add_listener(xdg_surface_.get(), &xdg_surface_listener, this); + xdg_toplevel_.reset(xdg_surface_get_toplevel(xdg_surface_.get())); + if (!xdg_toplevel_) { + LOG(ERROR) << "Can't get xdg toplevel"; + return false; + } + static const xdg_toplevel_listener xdg_toplevel_listener = { + [](void* data, struct xdg_toplevel* xdg_toplevel, int32_t width, + int32_t height, struct wl_array* states) {}, + [](void* data, struct xdg_toplevel* xdg_toplevel) {}}; + xdg_toplevel_add_listener(xdg_toplevel_.get(), &xdg_toplevel_listener, + this); } - static const xdg_surface_listener xdg_surface_listener = { - [](void* data, struct xdg_surface* xdg_surface, uint32_t layout_mode) { - xdg_surface_ack_configure(xdg_surface, layout_mode); - }, - }; - xdg_surface_add_listener(xdg_surface_.get(), &xdg_surface_listener, this); - xdg_toplevel_.reset(xdg_surface_get_toplevel(xdg_surface_.get())); - if (!xdg_toplevel_) { - LOG(ERROR) << "Can't get xdg toplevel"; - return false; - } - static const xdg_toplevel_listener xdg_toplevel_listener = { - [](void* data, struct xdg_toplevel* xdg_toplevel, int32_t width, - int32_t height, struct wl_array* states) {}, - [](void* data, struct xdg_toplevel* xdg_toplevel) {}}; - xdg_toplevel_add_listener(xdg_toplevel_.get(), &xdg_toplevel_listener, - this); if (fullscreen_) { LOG(ERROR) << "full screen not supported yet.";
diff --git a/components/exo/wayland/clients/client_base.h b/components/exo/wayland/clients/client_base.h index a2f92a0a..fab7537d 100644 --- a/components/exo/wayland/clients/client_base.h +++ b/components/exo/wayland/clients/client_base.h
@@ -203,6 +203,8 @@ std::unique_ptr<wl_shell_surface> shell_surface_; std::unique_ptr<xdg_surface> xdg_surface_; std::unique_ptr<xdg_toplevel> xdg_toplevel_; + std::unique_ptr<zxdg_surface_v6> zxdg_surface_; + std::unique_ptr<zxdg_toplevel_v6> zxdg_toplevel_; std::unique_ptr<wl_pointer> wl_pointer_; std::unique_ptr<zcr_pointer_stylus_v2> zcr_pointer_stylus_; Globals globals_;
diff --git a/components/exo/wayland/clients/client_helper.cc b/components/exo/wayland/clients/client_helper.cc index 3659802e..1dffd0e 100644 --- a/components/exo/wayland/clients/client_helper.cc +++ b/components/exo/wayland/clients/client_helper.cc
@@ -83,6 +83,7 @@ DEFAULT_DELETER(wp_content_type_manager_v1, wp_content_type_manager_v1_destroy) DEFAULT_DELETER(wp_content_type_v1, wp_content_type_v1_destroy) DEFAULT_DELETER(wp_viewporter, wp_viewporter_destroy) +DEFAULT_DELETER(zxdg_shell_v6, zxdg_shell_v6_destroy) DEFAULT_DELETER(xdg_wm_base, xdg_wm_base_destroy) DEFAULT_DELETER(zwp_text_input_manager_v1, zwp_text_input_manager_v1_destroy) DEFAULT_DELETER(zcr_secure_output_v1, zcr_secure_output_v1_destroy) @@ -111,6 +112,8 @@ DEFAULT_DELETER(zcr_extended_drag_v1, zcr_extended_drag_v1_destroy) DEFAULT_DELETER(xdg_surface, xdg_surface_destroy) DEFAULT_DELETER(xdg_toplevel, xdg_toplevel_destroy) +DEFAULT_DELETER(zxdg_surface_v6, zxdg_surface_v6_destroy) +DEFAULT_DELETER(zxdg_toplevel_v6, zxdg_toplevel_v6_destroy) DEFAULT_DELETER(zxdg_output_manager_v1, zxdg_output_manager_v1_destroy) DEFAULT_DELETER(weston_test, weston_test_destroy) DEFAULT_DELETER(zwp_idle_inhibit_manager_v1,
diff --git a/components/exo/wayland/clients/client_helper.h b/components/exo/wayland/clients/client_helper.h index cc2371a..c8c719f 100644 --- a/components/exo/wayland/clients/client_helper.h +++ b/components/exo/wayland/clients/client_helper.h
@@ -43,6 +43,7 @@ #include <xdg-decoration-unstable-v1-client-protocol.h> #include <xdg-output-unstable-v1-client-protocol.h> #include <xdg-shell-client-protocol.h> +#include <xdg-shell-unstable-v6-client-protocol.h> #include "base/scoped_generic.h" #if defined(USE_GBM) @@ -105,6 +106,7 @@ DEFAULT_DELETER_FDECL(wp_content_type_manager_v1) DEFAULT_DELETER_FDECL(wp_content_type_v1) DEFAULT_DELETER_FDECL(wp_viewporter) +DEFAULT_DELETER_FDECL(zxdg_shell_v6) DEFAULT_DELETER_FDECL(xdg_wm_base) DEFAULT_DELETER_FDECL(zwp_text_input_manager_v1) DEFAULT_DELETER_FDECL(zcr_secure_output_v1) @@ -129,6 +131,8 @@ DEFAULT_DELETER_FDECL(zcr_extended_drag_v1) DEFAULT_DELETER_FDECL(xdg_surface) DEFAULT_DELETER_FDECL(xdg_toplevel) +DEFAULT_DELETER_FDECL(zxdg_surface_v6) +DEFAULT_DELETER_FDECL(zxdg_toplevel_v6) DEFAULT_DELETER_FDECL(zxdg_output_manager_v1) DEFAULT_DELETER_FDECL(weston_test) DEFAULT_DELETER_FDECL(zwp_idle_inhibit_manager_v1)
diff --git a/components/exo/wayland/clients/globals.cc b/components/exo/wayland/clients/globals.cc index 8f965114..aab2033c 100644 --- a/components/exo/wayland/clients/globals.cc +++ b/components/exo/wayland/clients/globals.cc
@@ -53,6 +53,7 @@ BIND(wl_output, output) BIND(zwp_linux_explicit_synchronization_v1, linux_explicit_synchronization) BIND(zcr_vsync_feedback_v1, vsync_feedback) + BIND(zxdg_shell_v6, xdg_shell_v6) BIND(xdg_wm_base, xdg_wm_base) BIND(zcr_stylus_v2, stylus) BIND(zcr_remote_shell_v1, cr_remote_shell_v1)
diff --git a/components/exo/wayland/clients/globals.h b/components/exo/wayland/clients/globals.h index 654d1a2..31c9d41 100644 --- a/components/exo/wayland/clients/globals.h +++ b/components/exo/wayland/clients/globals.h
@@ -59,6 +59,7 @@ Object<wl_touch> touch; Object<zaura_shell> aura_shell; Object<zaura_output> aura_output; + Object<zxdg_shell_v6> xdg_shell_v6; Object<xdg_wm_base> xdg_wm_base; Object<zwp_fullscreen_shell_v1> fullscreen_shell; Object<zwp_input_timestamps_manager_v1> input_timestamps_manager;
diff --git a/components/exo/wayland/clients/security_delegate_binding_test.cc b/components/exo/wayland/clients/security_delegate_binding_test.cc index f5be747..3cd36cd7 100644 --- a/components/exo/wayland/clients/security_delegate_binding_test.cc +++ b/components/exo/wayland/clients/security_delegate_binding_test.cc
@@ -7,6 +7,7 @@ #include <wayland-client.h> #include <wayland-server.h> #include <xdg-shell-client-protocol.h> +#include <xdg-shell-unstable-v6-client-protocol.h> #include "components/exo/client_controlled_shell_surface.h" #include "components/exo/shell_surface.h" @@ -109,6 +110,43 @@ nullptr); } +TEST_F(SecurityDelegateBindingTest, ZxdgSurfaceV6HasSecurityDelegate) { + class ClientData : public test::TestClient::CustomData { + public: + std::unique_ptr<wl_surface> surface; + std::unique_ptr<zxdg_surface_v6> zxdg_surface; + }; + + test::ResourceKey zxdg_surface_key; + + PostToClientAndWait([&](test::TestClient* client) { + auto data = std::make_unique<ClientData>(); + + data->surface.reset(wl_compositor_create_surface(client->compositor())); + data->zxdg_surface.reset(zxdg_shell_v6_get_xdg_surface( + client->xdg_shell_v6(), data->surface.get())); + + zxdg_surface_key = + test::client_util::GetResourceKey(data->zxdg_surface.get()); + + client->set_data(std::move(data)); + }); + + EXPECT_EQ(test::server_util::GetUserDataForResource<WaylandXdgSurface>( + server_.get(), zxdg_surface_key) + ->shell_surface->GetSecurityDelegate(), + server_security_delegate_); + + PostToClientAndWait([](test::TestClient* client) { + // Destroy the client objects. + client->set_data(nullptr); + }); + + EXPECT_EQ(test::server_util::GetUserDataForResource<WaylandXdgSurface>( + server_.get(), zxdg_surface_key), + nullptr); +} + TEST_F(SecurityDelegateBindingTest, ZcrRemoteSurfaceV1HasSecurityDelegate) { class ClientData : public test::TestClient::CustomData { public:
diff --git a/components/exo/wayland/clients/test/client_version_test.cc b/components/exo/wayland/clients/test/client_version_test.cc index 441f7cfd7..e8c3f52 100644 --- a/components/exo/wayland/clients/test/client_version_test.cc +++ b/components/exo/wayland/clients/test/client_version_test.cc
@@ -35,6 +35,7 @@ #include <weston-test-server-protocol.h> #include <xdg-decoration-unstable-v1-server-protocol.h> #include <xdg-shell-server-protocol.h> +#include <xdg-shell-unstable-v6-server-protocol.h> #include <memory> #include <string> @@ -80,6 +81,7 @@ std::unique_ptr<wl_data_device_manager> wl_data_device_manager; std::unique_ptr<wp_content_type_manager_v1> wp_content_type_manager_v1; std::unique_ptr<wp_viewporter> wp_viewporter; + std::unique_ptr<zxdg_shell_v6> zxdg_shell_v6; std::unique_ptr<xdg_wm_base> xdg_wm_base; std::unique_ptr<zwp_text_input_manager_v1> zwp_text_input_manager_v1; std::unique_ptr<zcr_secure_output_v1> zcr_secure_output_v1; @@ -174,6 +176,7 @@ REGISTRY_CALLBACK(wp_content_type_manager_v1, wp_content_type_manager_v1), REGISTRY_CALLBACK(wp_viewporter, wp_viewporter), + REGISTRY_CALLBACK(zxdg_shell_v6, zxdg_shell_v6), REGISTRY_CALLBACK(xdg_wm_base, xdg_wm_base), REGISTRY_CALLBACK(zwp_text_input_manager_v1, zwp_text_input_manager_v1),
diff --git a/components/exo/wayland/compatibility_test/BUILD.gn b/components/exo/wayland/compatibility_test/BUILD.gn index ac1441f..61498f51 100644 --- a/components/exo/wayland/compatibility_test/BUILD.gn +++ b/components/exo/wayland/compatibility_test/BUILD.gn
@@ -20,6 +20,7 @@ "//third_party/wayland-protocols/src/unstable/pointer-gestures/pointer-gestures-unstable-v1.xml", "//third_party/wayland-protocols/src/unstable/relative-pointer/relative-pointer-unstable-v1.xml", "//third_party/wayland-protocols/src/unstable/text-input/text-input-unstable-v1.xml", + "//third_party/wayland-protocols/src/unstable/xdg-shell/xdg-shell-unstable-v6.xml", "//third_party/wayland-protocols/unstable/alpha-compositing/alpha-compositing-unstable-v1.xml", "//third_party/wayland-protocols/unstable/cursor-shapes/cursor-shapes-unstable-v1.xml", "//third_party/wayland-protocols/unstable/gaming-input/gaming-input-unstable-v2.xml",
diff --git a/components/exo/wayland/fuzzer/BUILD.gn b/components/exo/wayland/fuzzer/BUILD.gn index b485480..68e8641 100644 --- a/components/exo/wayland/fuzzer/BUILD.gn +++ b/components/exo/wayland/fuzzer/BUILD.gn
@@ -23,6 +23,7 @@ "//third_party/wayland-protocols/src/unstable/pointer-gestures/pointer-gestures-unstable-v1.xml", "//third_party/wayland-protocols/src/unstable/relative-pointer/relative-pointer-unstable-v1.xml", "//third_party/wayland-protocols/src/unstable/text-input/text-input-unstable-v1.xml", + "//third_party/wayland-protocols/src/unstable/xdg-shell/xdg-shell-unstable-v6.xml", "//third_party/wayland-protocols/unstable/alpha-compositing/alpha-compositing-unstable-v1.xml", "//third_party/wayland-protocols/unstable/cursor-shapes/cursor-shapes-unstable-v1.xml", "//third_party/wayland-protocols/unstable/gaming-input/gaming-input-unstable-v2.xml",
diff --git a/components/exo/wayland/fuzzer/wayland_sequencer.py b/components/exo/wayland/fuzzer/wayland_sequencer.py index 87f5256..c2e46f87 100644 --- a/components/exo/wayland/fuzzer/wayland_sequencer.py +++ b/components/exo/wayland/fuzzer/wayland_sequencer.py
@@ -181,7 +181,25 @@ c.RecordInterfaceCreated('wl_data_offer') e = SequenceBuilder('empty', dep_graph) - return [c, e] + + p = SequenceBuilder('popup_configuration', dep_graph) + p_positioner = p.BuildInterface('zxdg_positioner_v6') + p_parent = p.BuildInterface('zxdg_toplevel_v6') + p.AppendCall('zxdg_surface_v6', 'set_window_geometry', + [('receiver', p_parent), ('x', 0), ('y', 0), ('width', 10), + ('height', 10)]) + p.AppendRoundTrip() + p.AppendCall('wl_surface', 'commit', [('receiver', p_parent)]) + p.AppendRoundTrip() + p.AppendCall('zxdg_surface_v6', 'ack_configure', [('receiver', p_parent), + ('serial', 1)]) + p_child = p.BuildInterface('zxdg_surface_v6') + p.AppendCall('zxdg_surface_v6', 'get_popup', [('receiver', p_child), + ('parent', p_parent), + ('positioner', p_positioner)]) + p.AppendRoundTrip() + + return [c, e, p] def SequenceToTemplate(parsed_arguments, builder):
diff --git a/components/exo/wayland/server.cc b/components/exo/wayland/server.cc index dbcd6857..554eecc 100644 --- a/components/exo/wayland/server.cc +++ b/components/exo/wayland/server.cc
@@ -41,6 +41,7 @@ #include <xdg-decoration-unstable-v1-server-protocol.h> #include <xdg-output-unstable-v1-server-protocol.h> #include <xdg-shell-server-protocol.h> +#include <xdg-shell-unstable-v6-server-protocol.h> #include <linux-dmabuf-unstable-v1-server-protocol.h> #include <memory> @@ -106,6 +107,7 @@ #include "components/exo/wayland/zwp_text_input_manager.h" #include "components/exo/wayland/zxdg_decoration_manager.h" #include "components/exo/wayland/zxdg_output_manager.h" +#include "components/exo/wayland/zxdg_shell.h" #include "ui/display/display.h" #include "ui/display/screen.h" #include "ui/ozone/public/ozone_platform.h" @@ -430,6 +432,11 @@ zcr_text_input_extension_data_.get(), bind_text_input_extension); + zxdg_shell_data_ = + std::make_unique<WaylandZxdgShell>(display_, serial_tracker_.get()); + wl_global_create(wl_display_.get(), &zxdg_shell_v6_interface, 1, + zxdg_shell_data_.get(), bind_zxdg_shell_v6); + xdg_shell_data_ = std::make_unique<WaylandXdgShell>(display_, serial_tracker_.get()); wl_global_create(wl_display_.get(), &xdg_wm_base_interface, 3,
diff --git a/components/exo/wayland/server.h b/components/exo/wayland/server.h index 65d4e58..82b2a78 100644 --- a/components/exo/wayland/server.h +++ b/components/exo/wayland/server.h
@@ -35,6 +35,7 @@ struct WaylandTextInputExtension; struct WaylandTextInputManager; struct WaylandXdgShell; +struct WaylandZxdgShell; struct WaylandRemoteShellData; class WaylandDmabufFeedbackManager; class WestonTest; @@ -142,6 +143,7 @@ std::unique_ptr<WaylandKeyboardExtension> zcr_keyboard_extension_data_; std::unique_ptr<WaylandTextInputManager> zwp_text_manager_data_; std::unique_ptr<WaylandTextInputExtension> zcr_text_input_extension_data_; + std::unique_ptr<WaylandZxdgShell> zxdg_shell_data_; std::unique_ptr<WaylandXdgShell> xdg_shell_data_; std::unique_ptr<WaylandRemoteShellData> remote_shell_data_; std::unique_ptr<WestonTest> weston_test_holder_;
diff --git a/components/exo/wayland/test/test_client.h b/components/exo/wayland/test/test_client.h index 199549f2..3cedf4a 100644 --- a/components/exo/wayland/test/test_client.h +++ b/components/exo/wayland/test/test_client.h
@@ -62,6 +62,7 @@ wl_touch* touch() { return globals().touch.get(); } zaura_shell* aura_shell() { return globals().aura_shell.get(); } zaura_output* aura_output() { return globals().aura_output.get(); } + zxdg_shell_v6* xdg_shell_v6() { return globals().xdg_shell_v6.get(); } xdg_wm_base* xdg_wm_base() { return globals().xdg_wm_base.get(); } zwp_fullscreen_shell_v1* fullscreen_shell() { return globals().fullscreen_shell.get();
diff --git a/components/exo/wayland/wayland_positioner.cc b/components/exo/wayland/wayland_positioner.cc index 5b282f0..874a896 100644 --- a/components/exo/wayland/wayland_positioner.cc +++ b/components/exo/wayland/wayland_positioner.cc
@@ -6,12 +6,37 @@ #include <ostream> +#include <xdg-shell-unstable-v6-server-protocol.h> + namespace exo::wayland { namespace { std::pair<WaylandPositioner::Direction, WaylandPositioner::Direction> -DecomposeAnchor(uint32_t anchor) { +DecomposeUnstableAnchor(uint32_t anchor) { + WaylandPositioner::Direction x, y; + + if (anchor & ZXDG_POSITIONER_V6_ANCHOR_LEFT) { + x = WaylandPositioner::Direction::kNegative; + } else if (anchor & ZXDG_POSITIONER_V6_ANCHOR_RIGHT) { + x = WaylandPositioner::Direction::kPositive; + } else { + x = WaylandPositioner::Direction::kNeutral; + } + + if (anchor & ZXDG_POSITIONER_V6_ANCHOR_TOP) { + y = WaylandPositioner::Direction::kNegative; + } else if (anchor & ZXDG_POSITIONER_V6_ANCHOR_BOTTOM) { + y = WaylandPositioner::Direction::kPositive; + } else { + y = WaylandPositioner::Direction::kNeutral; + } + + return std::make_pair(x, y); +} + +std::pair<WaylandPositioner::Direction, WaylandPositioner::Direction> +DecomposeStableAnchor(uint32_t anchor) { switch (anchor) { default: case XDG_POSITIONER_ANCHOR_NONE: @@ -45,7 +70,30 @@ } std::pair<WaylandPositioner::Direction, WaylandPositioner::Direction> -DecomposeGravity(uint32_t gravity) { +DecomposeUnstableGravity(uint32_t gravity) { + WaylandPositioner::Direction x, y; + + if (gravity & ZXDG_POSITIONER_V6_GRAVITY_LEFT) { + x = WaylandPositioner::Direction::kNegative; + } else if (gravity & ZXDG_POSITIONER_V6_GRAVITY_RIGHT) { + x = WaylandPositioner::Direction::kPositive; + } else { + x = WaylandPositioner::Direction::kNeutral; + } + + if (gravity & ZXDG_POSITIONER_V6_GRAVITY_TOP) { + y = WaylandPositioner::Direction::kNegative; + } else if (gravity & ZXDG_POSITIONER_V6_GRAVITY_BOTTOM) { + y = WaylandPositioner::Direction::kPositive; + } else { + y = WaylandPositioner::Direction::kNeutral; + } + + return std::make_pair(x, y); +} + +std::pair<WaylandPositioner::Direction, WaylandPositioner::Direction> +DecomposeStableGravity(uint32_t gravity) { switch (gravity) { default: case XDG_POSITIONER_GRAVITY_NONE: @@ -289,7 +337,11 @@ void WaylandPositioner::SetAnchor(uint32_t anchor) { std::pair<WaylandPositioner::Direction, WaylandPositioner::Direction> decompose; - decompose = DecomposeAnchor(anchor); + if (version_ == UNSTABLE) { + decompose = DecomposeUnstableAnchor(anchor); + } else { + decompose = DecomposeStableAnchor(anchor); + } anchor_x_ = decompose.first; anchor_y_ = decompose.second; } @@ -297,7 +349,11 @@ void WaylandPositioner::SetGravity(uint32_t gravity) { std::pair<WaylandPositioner::Direction, WaylandPositioner::Direction> decompose; - decompose = DecomposeGravity(gravity); + if (version_ == UNSTABLE) { + decompose = DecomposeUnstableGravity(gravity); + } else { + decompose = DecomposeStableGravity(gravity); + } gravity_x_ = decompose.first; gravity_y_ = decompose.second; }
diff --git a/components/exo/wayland/wayland_positioner.h b/components/exo/wayland/wayland_positioner.h index 8f53d16d..7ff5143a 100644 --- a/components/exo/wayland/wayland_positioner.h +++ b/components/exo/wayland/wayland_positioner.h
@@ -26,7 +26,12 @@ // Represents the 1-dimensional projection of the gravity/anchor values. enum Direction { kNegative = -1, kNeutral = 0, kPositive = 1 }; - WaylandPositioner() = default; + // Controls whether anchor and gravity are set using the unstable bitfields or + // the stable enums. + enum Version { UNSTABLE, STABLE }; + + WaylandPositioner(Version v) : version_(v) {} + WaylandPositioner(const WaylandPositioner&) = delete; WaylandPositioner& operator=(const WaylandPositioner&) = delete; @@ -48,6 +53,8 @@ void SetOffset(gfx::Vector2d offset) { offset_ = std::move(offset); } private: + Version version_; + gfx::Size size_; gfx::Rect anchor_rect_; @@ -59,13 +66,13 @@ Direction gravity_y_ = kNeutral; // A bitmask that defines the subset of modifications to the position/size - // that are allowed, see xdg_positioner.constraint_adjustment() for more + // that are allowed, see zxdg_positioner.constraint_adjustment() for more // details. uint32_t adjustment_ = XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_NONE; // Defines an absolute translation (i.e. unaffected by flipping, scaling or // resizing) for the placement of the window relative to the |anchor_rect_|. - // See xdg_positioner.set_offset() for more details. + // See zxdg_positioner.set_offset() for more details. gfx::Vector2d offset_; };
diff --git a/components/exo/wayland/wayland_positioner_unittest.cc b/components/exo/wayland/wayland_positioner_unittest.cc index e44e474a..fcb283b 100644 --- a/components/exo/wayland/wayland_positioner_unittest.cc +++ b/components/exo/wayland/wayland_positioner_unittest.cc
@@ -7,6 +7,7 @@ #include "testing/gtest/include/gtest/gtest.h" #include "ui/gfx/geometry/rect.h" #include "xdg-shell-server-protocol.h" +#include "xdg-shell-unstable-v6-server-protocol.h" namespace exo { namespace wayland { @@ -20,7 +21,9 @@ WaylandPositioner positioner; gfx::Rect work_area = {0, 0, 5, 5}; - explicit TestCaseBuilder() { positioner.SetAnchorRect({2, 2, 1, 1}); } + explicit TestCaseBuilder(WaylandPositioner::Version v) : positioner(v) { + positioner.SetAnchorRect({2, 2, 1, 1}); + } TestCaseBuilder& SetFlipState(bool x, bool y) { return *this; @@ -68,31 +71,218 @@ }; }; -TEST_F(WaylandPositionerTest, UnconstrainedCases) { +// Tests for the unstable protocol. + +TEST_F(WaylandPositionerTest, UnconstrainedCasesUnstable) { // No gravity or anchor. - EXPECT_EQ(TestCaseBuilder().SetSize(1, 1).SolveToRect(), + EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::UNSTABLE) + .SetSize(1, 1) + .SolveToRect(), gfx::Rect(2, 2, 1, 1)); // Anchor without gravity. - EXPECT_EQ(TestCaseBuilder() + EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::UNSTABLE) + .SetSize(2, 1) + .SetAnchor(ZXDG_POSITIONER_V6_ANCHOR_RIGHT) + .SolveToRect(), + gfx::Rect(2, 2, 2, 1)); + EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::UNSTABLE) + .SetSize(2, 1) + .SetAnchor(ZXDG_POSITIONER_V6_ANCHOR_LEFT) + .SolveToRect(), + gfx::Rect(1, 2, 2, 1)); + + // Gravity without anchor. + EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::UNSTABLE) + .SetSize(1, 2) + .SetAnchorRect(2, 2, 0, 0) + .SetGravity(ZXDG_POSITIONER_V6_GRAVITY_TOP) + .SolveToRect(), + gfx::Rect(2, 0, 1, 2)); + EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::UNSTABLE) + .SetSize(1, 2) + .SetAnchorRect(2, 2, 0, 0) + .SetGravity(ZXDG_POSITIONER_V6_GRAVITY_BOTTOM) + .SolveToRect(), + gfx::Rect(2, 2, 1, 2)); + + // Gravity + anchor in the same direction. + EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::UNSTABLE) + .SetSize(2, 2) + .SetGravity(ZXDG_POSITIONER_V6_GRAVITY_BOTTOM | + ZXDG_POSITIONER_V6_GRAVITY_LEFT) + .SetAnchor(ZXDG_POSITIONER_V6_ANCHOR_BOTTOM | + ZXDG_POSITIONER_V6_ANCHOR_LEFT) + .SolveToRect(), + gfx::Rect(0, 3, 2, 2)); + + // Gravity + anchor in opposing directions. + EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::UNSTABLE) + .SetSize(2, 2) + .SetGravity(ZXDG_POSITIONER_V6_GRAVITY_BOTTOM | + ZXDG_POSITIONER_V6_GRAVITY_LEFT) + .SetAnchor(ZXDG_POSITIONER_V6_ANCHOR_TOP | + ZXDG_POSITIONER_V6_ANCHOR_RIGHT) + .SolveToRect(), + gfx::Rect(1, 2, 2, 2)); +} + +TEST_F(WaylandPositionerTest, FlipSlideResizePriorityUnstable) { + TestCaseBuilder builder{WaylandPositioner::Version::UNSTABLE}; + builder.SetAnchorRect(4, 4, 0, 0) + .SetSize(2, 2) + .SetGravity(ZXDG_POSITIONER_V6_GRAVITY_BOTTOM | + ZXDG_POSITIONER_V6_GRAVITY_RIGHT) + .SetAnchor(ZXDG_POSITIONER_V6_ANCHOR_BOTTOM | + ZXDG_POSITIONER_V6_ANCHOR_RIGHT); + // Flip is enabled, so the result will be at 2,2 (i.e. flipping a 2-wide + // square around 4,4). + EXPECT_EQ( + builder.SetAdjustment(~ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_NONE) + .SolveToRect(), + gfx::Rect(2, 2, 2, 2)); + // If we cant flip on an axis, that axis will slide to 3 instead. + EXPECT_EQ( + builder.SetAdjustment(~ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_FLIP_X) + .SolveToRect(), + gfx::Rect(3, 2, 2, 2)); + EXPECT_EQ( + builder.SetAdjustment(~ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_FLIP_Y) + .SolveToRect(), + gfx::Rect(2, 3, 2, 2)); + // If we cant flip or slide, we resize. + EXPECT_EQ( + builder + .SetAdjustment(ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_RESIZE_X | + ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_RESIZE_Y) + .SolveToRect(), + gfx::Rect(4, 4, 1, 1)); +} + +TEST_F(WaylandPositionerTest, TriesToMaximizeAreaUnstable) { + // The size is too large to fit where the anchor is. + WaylandPositioner::Result result = + TestCaseBuilder(WaylandPositioner::Version::UNSTABLE) + .SetAnchorRect(2, 4, 0, 0) + .SetSize(4, 10) + .SetGravity(ZXDG_POSITIONER_V6_GRAVITY_BOTTOM | + ZXDG_POSITIONER_V6_GRAVITY_RIGHT) + .SetAnchor(ZXDG_POSITIONER_V6_ANCHOR_BOTTOM | + ZXDG_POSITIONER_V6_ANCHOR_RIGHT) + .SetAdjustment(~ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_NONE) + .Solve(); + // We can slide to 1 on x, but we must resize on y (after sliding to 0). + EXPECT_EQ(result.origin, gfx::Point(1, 0)); + // The x size will be preserved but y shrinks to the work area. + EXPECT_EQ(result.size, gfx::Size(4, 5)); +} + +TEST_F(WaylandPositionerTest, PropagatesAnInitialFlipUnstable) { + WaylandPositioner::Result result = + TestCaseBuilder(WaylandPositioner::Version::UNSTABLE) + .SetAnchorRect(3, 1, 0, 0) + .SetSize(2, 2) + .SetGravity(ZXDG_POSITIONER_V6_GRAVITY_BOTTOM | + ZXDG_POSITIONER_V6_GRAVITY_RIGHT) + .SetAnchor(ZXDG_POSITIONER_V6_ANCHOR_BOTTOM | + ZXDG_POSITIONER_V6_ANCHOR_RIGHT) + .SetAdjustment(~ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_NONE) + .SetFlipState(true, true) + .Solve(); + // With a propagated flip state: + // - X and Y remain flipped to be positioned by the client. + EXPECT_EQ(result.origin, gfx::Point(3, 1)); + EXPECT_EQ(result.size, gfx::Size(2, 2)); +} + +// This is a common case for dropdown menus. In ChromeOS we do not let them +// slide if they might occlude the anchor rectangle. For this case, x axis does +// slide but the y axis resized instead. +TEST_F(WaylandPositionerTest, PreventsSlidingThatOccludesAnchorRectUnstable) { + EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::UNSTABLE) + .SetSize(3, 3) + .SetGravity(ZXDG_POSITIONER_V6_GRAVITY_BOTTOM | + ZXDG_POSITIONER_V6_GRAVITY_RIGHT) + .SetAnchor(ZXDG_POSITIONER_V6_ANCHOR_BOTTOM | + ZXDG_POSITIONER_V6_ANCHOR_LEFT) + .SetAdjustment(~ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_NONE) + .SolveToRect(), + gfx::Rect(2, 3, 3, 2)); + + // Here we ensure that the 4x4 popup does slide, which is allowed because + // the anchor rect is already occluded. + EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::UNSTABLE) + .SetSize(4, 4) + .SetGravity(ZXDG_POSITIONER_V6_GRAVITY_BOTTOM | + ZXDG_POSITIONER_V6_GRAVITY_RIGHT) + .SetAnchor(ZXDG_POSITIONER_V6_ANCHOR_TOP | + ZXDG_POSITIONER_V6_ANCHOR_LEFT) + .SetAdjustment(~ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_NONE) + .SolveToRect(), + gfx::Rect(1, 1, 4, 4)); +} + +// Allowing sliding which will occlude the anchor if there are no other +// positioning options which do not result in a constrained view available. +TEST_F(WaylandPositionerTest, + AllowsSlidingThatOccludesWhenThereAreNoOtherOptionsUnstable) { + EXPECT_EQ( + TestCaseBuilder(WaylandPositioner::Version::UNSTABLE) + .SetSize(4, 4) + .SetGravity(XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT) + .SetAnchor(XDG_POSITIONER_ANCHOR_BOTTOM_LEFT) + // Disable resizing in both axes which will force sliding. + .SetAdjustment(~(ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_RESIZE_X | + ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_RESIZE_Y)) + .SolveToRect(), + gfx::Rect(1, 1, 4, 4)); +} + +TEST_F(WaylandPositionerTest, + AllowsAdditionalAdjustmentsIfNoSolutionCanBeFoundUnstable) { + EXPECT_EQ( + TestCaseBuilder(WaylandPositioner::Version::UNSTABLE) + .SetWorkArea(gfx::Rect(5, 5)) + .SetSize(10, 10) + .SetAnchorRect(0, 0, 0, 0) + .SetGravity(ZXDG_POSITIONER_V6_GRAVITY_BOTTOM | + ZXDG_POSITIONER_V6_GRAVITY_RIGHT) + // No solution should forcibly allow resize + .SetAdjustment(ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_SLIDE_X | + ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_SLIDE_Y) + .SolveToRect(), + gfx::Rect(0, 0, 5, 5)); +} + +// Tests for the stable protocol. + +TEST_F(WaylandPositionerTest, UnconstrainedCases) { + // No gravity or anchor. + EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::STABLE) + .SetSize(1, 1) + .SolveToRect(), + gfx::Rect(2, 2, 1, 1)); + + // Anchor without gravity. + EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::STABLE) .SetSize(2, 1) .SetAnchor(XDG_POSITIONER_ANCHOR_RIGHT) .SolveToRect(), gfx::Rect(2, 2, 2, 1)); - EXPECT_EQ(TestCaseBuilder() + EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::STABLE) .SetSize(2, 1) .SetAnchor(XDG_POSITIONER_ANCHOR_LEFT) .SolveToRect(), gfx::Rect(1, 2, 2, 1)); // Gravity without anchor. - EXPECT_EQ(TestCaseBuilder() + EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::STABLE) .SetSize(1, 2) .SetAnchorRect(2, 2, 0, 0) .SetGravity(XDG_POSITIONER_GRAVITY_TOP) .SolveToRect(), gfx::Rect(2, 0, 1, 2)); - EXPECT_EQ(TestCaseBuilder() + EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::STABLE) .SetSize(1, 2) .SetAnchorRect(2, 2, 0, 0) .SetGravity(XDG_POSITIONER_GRAVITY_BOTTOM) @@ -100,7 +290,7 @@ gfx::Rect(2, 2, 1, 2)); // Gravity + anchor in the same direction. - EXPECT_EQ(TestCaseBuilder() + EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::STABLE) .SetSize(2, 2) .SetGravity(XDG_POSITIONER_GRAVITY_BOTTOM_LEFT) .SetAnchor(XDG_POSITIONER_ANCHOR_BOTTOM_LEFT) @@ -108,7 +298,7 @@ gfx::Rect(0, 3, 2, 2)); // Gravity + anchor in opposing directions. - EXPECT_EQ(TestCaseBuilder() + EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::STABLE) .SetSize(2, 2) .SetGravity(XDG_POSITIONER_GRAVITY_BOTTOM_LEFT) .SetAnchor(XDG_POSITIONER_ANCHOR_TOP_RIGHT) @@ -117,7 +307,7 @@ } TEST_F(WaylandPositionerTest, FlipSlideResizePriority) { - TestCaseBuilder builder; + TestCaseBuilder builder{WaylandPositioner::Version::STABLE}; builder.SetAnchorRect(4, 4, 0, 0) .SetSize(2, 2) .SetGravity(XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT) @@ -145,7 +335,7 @@ TEST_F(WaylandPositionerTest, TriesToMaximizeArea) { // The size is too large to fit where the anchor is. WaylandPositioner::Result result = - TestCaseBuilder() + TestCaseBuilder(WaylandPositioner::Version::STABLE) .SetAnchorRect(2, 4, 0, 0) .SetSize(4, 10) .SetGravity(XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT) @@ -160,7 +350,7 @@ TEST_F(WaylandPositionerTest, PropagatesAnInitialFlip) { WaylandPositioner::Result result = - TestCaseBuilder() + TestCaseBuilder(WaylandPositioner::Version::STABLE) .SetAnchorRect(3, 1, 0, 0) .SetSize(2, 2) .SetGravity(XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT) @@ -178,7 +368,7 @@ // slide if they might occlude the anchor rectangle. For this case, x axis does // slide but the y axis resized instead. TEST_F(WaylandPositionerTest, PreventsSlidingThatOccludesAnchorRect) { - EXPECT_EQ(TestCaseBuilder() + EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::STABLE) .SetSize(3, 3) .SetGravity(XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT) .SetAnchor(XDG_POSITIONER_ANCHOR_BOTTOM_LEFT) @@ -188,7 +378,7 @@ // Here we ensure that the 4x4 popup does slide, which is allowed because // the anchor rect is already occluded. - EXPECT_EQ(TestCaseBuilder() + EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::STABLE) .SetSize(4, 4) .SetGravity(XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT) .SetAnchor(XDG_POSITIONER_ANCHOR_TOP_LEFT) @@ -201,7 +391,7 @@ // positioning options which do not result in a constrained view available. TEST_F(WaylandPositionerTest, AllowsSlidingThatOccludesWhenThereAreNoOtherOptions) { - EXPECT_EQ(TestCaseBuilder() + EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::STABLE) .SetSize(4, 4) .SetGravity(XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT) .SetAnchor(XDG_POSITIONER_ANCHOR_BOTTOM_LEFT) @@ -215,7 +405,7 @@ // Make sure that the size should never be an empty even if the constraints // resulted in empty size. TEST_F(WaylandPositionerTest, ResizableShouldNotBeEmpty) { - EXPECT_EQ(TestCaseBuilder() + EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::STABLE) .SetSize(3, 3) .SetGravity(XDG_POSITIONER_GRAVITY_BOTTOM) .SetAnchor(XDG_POSITIONER_ANCHOR_BOTTOM) @@ -223,7 +413,7 @@ .SetAnchorRect(1, -10, 4, 4) .SolveToRect(), gfx::Rect(2, 0, 3, 1)); - EXPECT_EQ(TestCaseBuilder() + EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::STABLE) .SetSize(3, 3) .SetGravity(XDG_POSITIONER_GRAVITY_RIGHT) .SetAnchor(XDG_POSITIONER_ANCHOR_RIGHT) @@ -235,7 +425,7 @@ TEST_F(WaylandPositionerTest, AllowsAdditionalAdjustmentsIfNoSolutionCanBeFound) { - EXPECT_EQ(TestCaseBuilder() + EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::STABLE) .SetWorkArea(gfx::Rect(5, 5)) .SetSize(10, 10) .SetAnchorRect(0, 0, 0, 0)
diff --git a/components/exo/wayland/xdg_shell.cc b/components/exo/wayland/xdg_shell.cc index 560e959..0fc7cf8d 100644 --- a/components/exo/wayland/xdg_shell.cc +++ b/components/exo/wayland/xdg_shell.cc
@@ -730,8 +730,9 @@ wl_resource* positioner_resource = wl_resource_create( client, &xdg_positioner_interface, wl_resource_get_version(resource), id); - SetImplementation(positioner_resource, &xdg_positioner_implementation, - std::make_unique<WaylandPositioner>()); + SetImplementation( + positioner_resource, &xdg_positioner_implementation, + std::make_unique<WaylandPositioner>(WaylandPositioner::Version::STABLE)); } void xdg_wm_base_get_xdg_surface(wl_client* client,
diff --git a/components/exo/wayland/zxdg_shell.cc b/components/exo/wayland/zxdg_shell.cc new file mode 100644 index 0000000..80ad3db --- /dev/null +++ b/components/exo/wayland/zxdg_shell.cc
@@ -0,0 +1,690 @@ +// Copyright 2018 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/exo/wayland/zxdg_shell.h" + +#include <wayland-server-core.h> +#include <wayland-server-protocol-core.h> +#include <xdg-shell-unstable-v6-server-protocol.h> + +#include "ash/public/cpp/shell_window_ids.h" +#include "ash/public/cpp/window_properties.h" +#include "base/functional/bind.h" +#include "base/strings/utf_string_conversions.h" +#include "chromeos/ui/base/window_state_type.h" +#include "components/exo/display.h" +#include "components/exo/shell_surface_util.h" +#include "components/exo/wayland/serial_tracker.h" +#include "components/exo/wayland/server_util.h" +#include "components/exo/wayland/wayland_positioner.h" +#include "components/exo/wayland/xdg_shell.h" +#include "components/exo/xdg_shell_surface.h" +#include "ui/aura/window_observer.h" +#include "ui/base/hit_test.h" +#include "ui/display/screen.h" +#include "ui/views/widget/widget.h" +#include "ui/wm/core/coordinate_conversion.h" + +namespace exo { +namespace wayland { +namespace { + +//////////////////////////////////////////////////////////////////////////////// +// xdg_positioner_interface: + +void xdg_positioner_v6_destroy(wl_client* client, wl_resource* resource) { + wl_resource_destroy(resource); +} + +void xdg_positioner_v6_set_size(wl_client* client, + wl_resource* resource, + int32_t width, + int32_t height) { + if (width < 1 || height < 1) { + wl_resource_post_error(resource, ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT, + "width and height must be positive and non-zero"); + return; + } + + GetUserDataAs<WaylandPositioner>(resource)->SetSize(gfx::Size(width, height)); +} + +void xdg_positioner_v6_set_anchor_rect(wl_client* client, + wl_resource* resource, + int32_t x, + int32_t y, + int32_t width, + int32_t height) { + if (width < 1 || height < 1) { + wl_resource_post_error(resource, ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT, + "width and height must be positive and non-zero"); + return; + } + + GetUserDataAs<WaylandPositioner>(resource)->SetAnchorRect( + gfx::Rect(x, y, width, height)); +} + +void xdg_positioner_v6_set_anchor(wl_client* client, + wl_resource* resource, + uint32_t anchor) { + if (((anchor & ZXDG_POSITIONER_V6_ANCHOR_LEFT) && + (anchor & ZXDG_POSITIONER_V6_ANCHOR_RIGHT)) || + ((anchor & ZXDG_POSITIONER_V6_ANCHOR_TOP) && + (anchor & ZXDG_POSITIONER_V6_ANCHOR_BOTTOM))) { + wl_resource_post_error(resource, ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT, + "same-axis values are not allowed"); + return; + } + + GetUserDataAs<WaylandPositioner>(resource)->SetAnchor(anchor); +} + +void xdg_positioner_v6_set_gravity(wl_client* client, + wl_resource* resource, + uint32_t gravity) { + if (((gravity & ZXDG_POSITIONER_V6_GRAVITY_LEFT) && + (gravity & ZXDG_POSITIONER_V6_GRAVITY_RIGHT)) || + ((gravity & ZXDG_POSITIONER_V6_GRAVITY_TOP) && + (gravity & ZXDG_POSITIONER_V6_GRAVITY_BOTTOM))) { + wl_resource_post_error(resource, ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT, + "same-axis values are not allowed"); + return; + } + + GetUserDataAs<WaylandPositioner>(resource)->SetGravity(gravity); +} + +void xdg_positioner_v6_set_constraint_adjustment(wl_client* client, + wl_resource* resource, + uint32_t adjustment) { + GetUserDataAs<WaylandPositioner>(resource)->SetAdjustment(adjustment); +} + +void xdg_positioner_v6_set_offset(wl_client* client, + wl_resource* resource, + int32_t x, + int32_t y) { + GetUserDataAs<WaylandPositioner>(resource)->SetOffset(gfx::Vector2d(x, y)); +} + +const struct zxdg_positioner_v6_interface xdg_positioner_v6_implementation = { + xdg_positioner_v6_destroy, + xdg_positioner_v6_set_size, + xdg_positioner_v6_set_anchor_rect, + xdg_positioner_v6_set_anchor, + xdg_positioner_v6_set_gravity, + xdg_positioner_v6_set_constraint_adjustment, + xdg_positioner_v6_set_offset}; + +//////////////////////////////////////////////////////////////////////////////// +// xdg_toplevel_interface: + +int XdgToplevelV6ResizeComponent(uint32_t edges) { + switch (edges) { + case ZXDG_TOPLEVEL_V6_RESIZE_EDGE_TOP: + return HTTOP; + case ZXDG_TOPLEVEL_V6_RESIZE_EDGE_BOTTOM: + return HTBOTTOM; + case ZXDG_TOPLEVEL_V6_RESIZE_EDGE_LEFT: + return HTLEFT; + case ZXDG_TOPLEVEL_V6_RESIZE_EDGE_TOP_LEFT: + return HTTOPLEFT; + case ZXDG_TOPLEVEL_V6_RESIZE_EDGE_BOTTOM_LEFT: + return HTBOTTOMLEFT; + case ZXDG_TOPLEVEL_V6_RESIZE_EDGE_RIGHT: + return HTRIGHT; + case ZXDG_TOPLEVEL_V6_RESIZE_EDGE_TOP_RIGHT: + return HTTOPRIGHT; + case ZXDG_TOPLEVEL_V6_RESIZE_EDGE_BOTTOM_RIGHT: + return HTBOTTOMRIGHT; + default: + return HTBOTTOMRIGHT; + } +} + +using XdgSurfaceConfigureCallback = + base::RepeatingCallback<void(const gfx::Size& size, + chromeos::WindowStateType state_type, + bool resizing, + bool activated)>; + +uint32_t HandleXdgSurfaceV6ConfigureCallback( + wl_resource* resource, + SerialTracker* serial_tracker, + const XdgSurfaceConfigureCallback& callback, + const gfx::Rect& bounds, + chromeos::WindowStateType state_type, + bool resizing, + bool activated, + const gfx::Vector2d& origin_offset, + float raster_scale) { + uint32_t serial = + serial_tracker->GetNextSerial(SerialTracker::EventType::OTHER_EVENT); + callback.Run(bounds.size(), state_type, resizing, activated); + zxdg_surface_v6_send_configure(resource, serial); + wl_client_flush(wl_resource_get_client(resource)); + return serial; +} + +// Wrapper around shell surface that allows us to handle the case where the +// xdg surface resource is destroyed before the toplevel resource. +class WaylandToplevel : public aura::WindowObserver { + public: + WaylandToplevel(wl_resource* resource, wl_resource* surface_resource) + : resource_(resource), + shell_surface_data_( + GetUserDataAs<WaylandXdgSurface>(surface_resource)) { + shell_surface_data_->shell_surface->host_window()->AddObserver(this); + shell_surface_data_->shell_surface->set_close_callback(base::BindRepeating( + &WaylandToplevel::OnClose, weak_ptr_factory_.GetWeakPtr())); + shell_surface_data_->shell_surface->set_configure_callback( + base::BindRepeating( + &HandleXdgSurfaceV6ConfigureCallback, surface_resource, + shell_surface_data_->serial_tracker, + base::BindRepeating(&WaylandToplevel::OnConfigure, + weak_ptr_factory_.GetWeakPtr()))); + } + + WaylandToplevel(const WaylandToplevel&) = delete; + WaylandToplevel& operator=(const WaylandToplevel&) = delete; + + ~WaylandToplevel() override { + if (shell_surface_data_) + shell_surface_data_->shell_surface->host_window()->RemoveObserver(this); + } + + // Overridden from aura::WindowObserver: + void OnWindowDestroying(aura::Window* window) override { + shell_surface_data_ = nullptr; + } + + void SetParent(WaylandToplevel* parent) { + if (!shell_surface_data_) + return; + + if (!parent) { + shell_surface_data_->shell_surface->SetParent(nullptr); + return; + } + + if (this == parent) { + // Some apps e.g. crbug/1210235 try to be their own parent. Ignore them. + auto* app_id = GetShellApplicationId( + shell_surface_data_->shell_surface->host_window()); + LOG(WARNING) + << "Client attempts to add itself as a transient parent: app_id=" + << app_id; + return; + } + + // This is a no-op if parent is not mapped. + if (parent->shell_surface_data_ && + parent->shell_surface_data_->shell_surface->GetWidget()) + shell_surface_data_->shell_surface->SetParent( + parent->shell_surface_data_->shell_surface.get()); + } + + void SetTitle(const std::u16string& title) { + if (shell_surface_data_) + shell_surface_data_->shell_surface->SetTitle(title); + } + + void SetApplicationId(const char* application_id) { + if (shell_surface_data_) + shell_surface_data_->shell_surface->SetApplicationId(application_id); + } + + void Move() { + if (shell_surface_data_) + shell_surface_data_->shell_surface->StartMove(); + } + + void Resize(int component) { + if (!shell_surface_data_) + return; + + if (component != HTNOWHERE) + shell_surface_data_->shell_surface->StartResize(component); + } + + void SetMaximumSize(const gfx::Size& size) { + if (shell_surface_data_) + shell_surface_data_->shell_surface->SetMaximumSize(size); + } + + void SetMinimumSize(const gfx::Size& size) { + if (shell_surface_data_) + shell_surface_data_->shell_surface->SetMinimumSize(size); + } + + void Maximize() { + if (shell_surface_data_) + shell_surface_data_->shell_surface->Maximize(); + } + + void Restore() { + if (shell_surface_data_) + shell_surface_data_->shell_surface->Restore(); + } + + void SetFullscreen(bool fullscreen) { + if (shell_surface_data_) + shell_surface_data_->shell_surface->SetFullscreen(fullscreen); + } + + void Minimize() { + if (shell_surface_data_) + shell_surface_data_->shell_surface->Minimize(); + } + + private: + void OnClose() { + zxdg_toplevel_v6_send_close(resource_); + wl_client_flush(wl_resource_get_client(resource_)); + } + + static void AddState(wl_array* states, zxdg_toplevel_v6_state state) { + zxdg_toplevel_v6_state* value = static_cast<zxdg_toplevel_v6_state*>( + wl_array_add(states, sizeof(zxdg_toplevel_v6_state))); + DCHECK(value); + *value = state; + } + + void OnConfigure(const gfx::Size& size, + chromeos::WindowStateType state_type, + bool resizing, + bool activated) { + wl_array states; + wl_array_init(&states); + if (state_type == chromeos::WindowStateType::kMaximized) + AddState(&states, ZXDG_TOPLEVEL_V6_STATE_MAXIMIZED); + if (state_type == chromeos::WindowStateType::kFullscreen) + AddState(&states, ZXDG_TOPLEVEL_V6_STATE_FULLSCREEN); + if (resizing) + AddState(&states, ZXDG_TOPLEVEL_V6_STATE_RESIZING); + if (activated) + AddState(&states, ZXDG_TOPLEVEL_V6_STATE_ACTIVATED); + zxdg_toplevel_v6_send_configure(resource_, size.width(), size.height(), + &states); + wl_array_release(&states); + } + + wl_resource* const resource_; + WaylandXdgSurface* shell_surface_data_; + base::WeakPtrFactory<WaylandToplevel> weak_ptr_factory_{this}; +}; + +void xdg_toplevel_v6_destroy(wl_client* client, wl_resource* resource) { + wl_resource_destroy(resource); +} + +void xdg_toplevel_v6_set_parent(wl_client* client, + wl_resource* resource, + wl_resource* parent) { + WaylandToplevel* parent_surface = nullptr; + if (parent) + parent_surface = GetUserDataAs<WaylandToplevel>(parent); + + GetUserDataAs<WaylandToplevel>(resource)->SetParent(parent_surface); +} + +void xdg_toplevel_v6_set_title(wl_client* client, + wl_resource* resource, + const char* title) { + GetUserDataAs<WaylandToplevel>(resource)->SetTitle( + std::u16string(base::UTF8ToUTF16(title))); +} + +void xdg_toplevel_v6_set_app_id(wl_client* client, + wl_resource* resource, + const char* app_id) { + GetUserDataAs<WaylandToplevel>(resource)->SetApplicationId(app_id); +} + +void xdg_toplevel_v6_show_window_menu(wl_client* client, + wl_resource* resource, + wl_resource* seat, + uint32_t serial, + int32_t x, + int32_t y) { + NOTIMPLEMENTED(); +} + +void xdg_toplevel_v6_move(wl_client* client, + wl_resource* resource, + wl_resource* seat, + uint32_t serial) { + GetUserDataAs<WaylandToplevel>(resource)->Move(); +} + +void xdg_toplevel_v6_resize(wl_client* client, + wl_resource* resource, + wl_resource* seat, + uint32_t serial, + uint32_t edges) { + GetUserDataAs<WaylandToplevel>(resource)->Resize( + XdgToplevelV6ResizeComponent(edges)); +} + +void xdg_toplevel_v6_set_max_size(wl_client* client, + wl_resource* resource, + int32_t width, + int32_t height) { + GetUserDataAs<WaylandToplevel>(resource)->SetMaximumSize( + gfx::Size(width, height)); +} + +void xdg_toplevel_v6_set_min_size(wl_client* client, + wl_resource* resource, + int32_t width, + int32_t height) { + GetUserDataAs<WaylandToplevel>(resource)->SetMinimumSize( + gfx::Size(width, height)); +} + +void xdg_toplevel_v6_set_maximized(wl_client* client, wl_resource* resource) { + GetUserDataAs<WaylandToplevel>(resource)->Maximize(); +} + +void xdg_toplevel_v6_unset_maximized(wl_client* client, wl_resource* resource) { + GetUserDataAs<WaylandToplevel>(resource)->Restore(); +} + +void xdg_toplevel_v6_set_fullscreen(wl_client* client, + wl_resource* resource, + wl_resource* output) { + GetUserDataAs<WaylandToplevel>(resource)->SetFullscreen(true); +} + +void xdg_toplevel_v6_unset_fullscreen(wl_client* client, + wl_resource* resource) { + GetUserDataAs<WaylandToplevel>(resource)->SetFullscreen(false); +} + +void xdg_toplevel_v6_set_minimized(wl_client* client, wl_resource* resource) { + GetUserDataAs<WaylandToplevel>(resource)->Minimize(); +} + +const struct zxdg_toplevel_v6_interface xdg_toplevel_v6_implementation = { + xdg_toplevel_v6_destroy, xdg_toplevel_v6_set_parent, + xdg_toplevel_v6_set_title, xdg_toplevel_v6_set_app_id, + xdg_toplevel_v6_show_window_menu, xdg_toplevel_v6_move, + xdg_toplevel_v6_resize, xdg_toplevel_v6_set_max_size, + xdg_toplevel_v6_set_min_size, xdg_toplevel_v6_set_maximized, + xdg_toplevel_v6_unset_maximized, xdg_toplevel_v6_set_fullscreen, + xdg_toplevel_v6_unset_fullscreen, xdg_toplevel_v6_set_minimized}; + +//////////////////////////////////////////////////////////////////////////////// +// xdg_popup_interface: + +// Wrapper around shell surface that allows us to handle the case where the +// xdg surface resource is destroyed before the popup resource. +class WaylandPopup : aura::WindowObserver { + public: + WaylandPopup(wl_resource* resource, wl_resource* surface_resource) + : resource_(resource), + shell_surface_data_( + GetUserDataAs<WaylandXdgSurface>(surface_resource)) { + shell_surface_data_->shell_surface->host_window()->AddObserver(this); + shell_surface_data_->shell_surface->set_close_callback(base::BindRepeating( + &WaylandPopup::OnClose, weak_ptr_factory_.GetWeakPtr())); + shell_surface_data_->shell_surface->set_configure_callback( + base::BindRepeating( + &HandleXdgSurfaceV6ConfigureCallback, surface_resource, + shell_surface_data_->serial_tracker, + base::BindRepeating(&WaylandPopup::OnConfigure, + weak_ptr_factory_.GetWeakPtr()))); + } + + WaylandPopup(const WaylandPopup&) = delete; + WaylandPopup& operator=(const WaylandPopup&) = delete; + + ~WaylandPopup() override { + if (shell_surface_data_) + shell_surface_data_->shell_surface->host_window()->RemoveObserver(this); + } + + void Grab() { + if (!shell_surface_data_) { + wl_resource_post_error(resource_, ZXDG_POPUP_V6_ERROR_INVALID_GRAB, + "the surface has already been destroyed"); + return; + } + if (shell_surface_data_->shell_surface->GetWidget()) { + wl_resource_post_error(resource_, ZXDG_POPUP_V6_ERROR_INVALID_GRAB, + "grab must be called before construction"); + return; + } + shell_surface_data_->shell_surface->Grab(); + } + + // Overridden from aura::WindowObserver: + void OnWindowDestroying(aura::Window* window) override { + shell_surface_data_ = nullptr; + } + + private: + void OnClose() { + zxdg_popup_v6_send_popup_done(resource_); + wl_client_flush(wl_resource_get_client(resource_)); + } + + void OnConfigure(const gfx::Size& size, + chromeos::WindowStateType state_type, + bool resizing, + bool activated) { + // Nothing to do here as popups don't have additional configure state. + } + + wl_resource* const resource_; + WaylandXdgSurface* shell_surface_data_; + base::WeakPtrFactory<WaylandPopup> weak_ptr_factory_{this}; +}; + +void xdg_popup_v6_destroy(wl_client* client, wl_resource* resource) { + wl_resource_destroy(resource); +} + +void xdg_popup_v6_grab(wl_client* client, + wl_resource* resource, + wl_resource* seat, + uint32_t serial) { + GetUserDataAs<WaylandPopup>(resource)->Grab(); +} + +const struct zxdg_popup_v6_interface xdg_popup_v6_implementation = { + xdg_popup_v6_destroy, xdg_popup_v6_grab}; + +//////////////////////////////////////////////////////////////////////////////// +// xdg_surface_interface: + +void xdg_surface_v6_destroy(wl_client* client, wl_resource* resource) { + wl_resource_destroy(resource); +} + +void xdg_surface_v6_get_toplevel(wl_client* client, + wl_resource* resource, + uint32_t id) { + auto* shell_surface_data = GetUserDataAs<WaylandXdgSurface>(resource); + if (shell_surface_data->shell_surface->GetEnabled()) { + wl_resource_post_error(resource, ZXDG_SURFACE_V6_ERROR_ALREADY_CONSTRUCTED, + "surface has already been constructed"); + return; + } + + shell_surface_data->shell_surface->SetCanMinimize(true); + shell_surface_data->shell_surface->SetEnabled(true); + + wl_resource* xdg_toplevel_resource = + wl_resource_create(client, &zxdg_toplevel_v6_interface, 1, id); + + SetImplementation( + xdg_toplevel_resource, &xdg_toplevel_v6_implementation, + std::make_unique<WaylandToplevel>(xdg_toplevel_resource, resource)); +} + +void xdg_surface_v6_get_popup(wl_client* client, + wl_resource* resource, + uint32_t id, + wl_resource* parent_resource, + wl_resource* positioner_resource) { + auto* shell_surface_data = GetUserDataAs<WaylandXdgSurface>(resource); + if (shell_surface_data->shell_surface->GetEnabled()) { + wl_resource_post_error(resource, ZXDG_SURFACE_V6_ERROR_ALREADY_CONSTRUCTED, + "surface has already been constructed"); + return; + } + + auto* parent_data = GetUserDataAs<WaylandXdgSurface>(parent_resource); + if (!parent_data->shell_surface->GetWidget()) { + wl_resource_post_error(resource, ZXDG_SURFACE_V6_ERROR_NOT_CONSTRUCTED, + "popup parent not constructed"); + return; + } + + if (shell_surface_data->shell_surface->GetWidget()) { + wl_resource_post_error(resource, ZXDG_SURFACE_V6_ERROR_ALREADY_CONSTRUCTED, + "get_popup is called after constructed"); + return; + } + + display::Display display = + display::Screen::GetScreen()->GetDisplayNearestWindow( + parent_data->shell_surface->GetWidget()->GetNativeWindow()); + gfx::Rect work_area = display.work_area(); + wm::ConvertRectFromScreen( + parent_data->shell_surface->GetWidget()->GetNativeWindow(), &work_area); + + // Try layout using parent's flip state. + WaylandPositioner* positioner = + GetUserDataAs<WaylandPositioner>(positioner_resource); + WaylandPositioner::Result position = positioner->CalculateBounds(work_area); + + // |position| is relative to the parent's contents view origin, and |origin| + // is in screen coordinates. + gfx::Point origin = position.origin; + views::View::ConvertPointToScreen(parent_data->shell_surface->GetWidget() + ->widget_delegate() + ->GetContentsView(), + &origin); + shell_surface_data->shell_surface->SetOrigin(origin); + shell_surface_data->shell_surface->SetSize(position.size); + shell_surface_data->shell_surface->DisableMovement(); + shell_surface_data->shell_surface->SetActivatable(false); + shell_surface_data->shell_surface->SetCanMinimize(false); + shell_surface_data->shell_surface->SetParent( + parent_data->shell_surface.get()); + shell_surface_data->shell_surface->SetPopup(); + shell_surface_data->shell_surface->SetEnabled(true); + + wl_resource* xdg_popup_resource = + wl_resource_create(client, &zxdg_popup_v6_interface, 1, id); + + SetImplementation( + xdg_popup_resource, &xdg_popup_v6_implementation, + std::make_unique<WaylandPopup>(xdg_popup_resource, resource)); + + // We send the configure event here as this event needs x,y coordinates + // relative to the parent window. + zxdg_popup_v6_send_configure(xdg_popup_resource, position.origin.x(), + position.origin.y(), position.size.width(), + position.size.height()); +} + +void xdg_surface_v6_set_window_geometry(wl_client* client, + wl_resource* resource, + int32_t x, + int32_t y, + int32_t width, + int32_t height) { + GetUserDataAs<WaylandXdgSurface>(resource)->shell_surface->SetGeometry( + gfx::Rect(x, y, width, height)); +} + +void xdg_surface_v6_ack_configure(wl_client* client, + wl_resource* resource, + uint32_t serial) { + GetUserDataAs<WaylandXdgSurface>(resource) + ->shell_surface->AcknowledgeConfigure(serial); +} + +const struct zxdg_surface_v6_interface xdg_surface_v6_implementation = { + xdg_surface_v6_destroy, xdg_surface_v6_get_toplevel, + xdg_surface_v6_get_popup, xdg_surface_v6_set_window_geometry, + xdg_surface_v6_ack_configure}; + +//////////////////////////////////////////////////////////////////////////////// +// xdg_shell_interface: + +void xdg_shell_v6_destroy(wl_client* client, wl_resource* resource) { + // Nothing to do here. +} + +void xdg_shell_v6_create_positioner(wl_client* client, + wl_resource* resource, + uint32_t id) { + wl_resource* positioner_resource = + wl_resource_create(client, &zxdg_positioner_v6_interface, 1, id); + + SetImplementation(positioner_resource, &xdg_positioner_v6_implementation, + std::make_unique<WaylandPositioner>( + WaylandPositioner::Version::UNSTABLE)); +} + +void xdg_shell_v6_get_xdg_surface(wl_client* client, + wl_resource* resource, + uint32_t id, + wl_resource* surface) { + auto* data = GetUserDataAs<WaylandZxdgShell>(resource); + std::unique_ptr<XdgShellSurface> shell_surface = + data->display->CreateXdgShellSurface(GetUserDataAs<Surface>(surface)); + if (!shell_surface) { + wl_resource_post_error(resource, ZXDG_SHELL_V6_ERROR_ROLE, + "surface has already been assigned a role"); + return; + } + + // Xdg shell v6 surfaces are initially disabled and needs to be explicitly + // mapped before they are enabled and can become visible. + shell_surface->SetEnabled(false); + + shell_surface->SetSecurityDelegate(GetSecurityDelegate(client)); + + std::unique_ptr<WaylandXdgSurface> wayland_shell_surface = + std::make_unique<WaylandXdgSurface>(std::move(shell_surface), + data->serial_tracker); + + wl_resource* xdg_surface_resource = + wl_resource_create(client, &zxdg_surface_v6_interface, 1, id); + + SetImplementation(xdg_surface_resource, &xdg_surface_v6_implementation, + std::move(wayland_shell_surface)); +} + +void xdg_shell_v6_pong(wl_client* client, + wl_resource* resource, + uint32_t serial) { + NOTIMPLEMENTED(); +} + +const struct zxdg_shell_v6_interface xdg_shell_v6_implementation = { + xdg_shell_v6_destroy, xdg_shell_v6_create_positioner, + xdg_shell_v6_get_xdg_surface, xdg_shell_v6_pong}; + +} // namespace + +void bind_zxdg_shell_v6(wl_client* client, + void* data, + uint32_t version, + uint32_t id) { + wl_resource* resource = + wl_resource_create(client, &zxdg_shell_v6_interface, 1, id); + + wl_resource_set_implementation(resource, &xdg_shell_v6_implementation, data, + nullptr); +} + +} // namespace wayland +} // namespace exo
diff --git a/components/exo/wayland/zxdg_shell.h b/components/exo/wayland/zxdg_shell.h new file mode 100644 index 0000000..4eadfdd --- /dev/null +++ b/components/exo/wayland/zxdg_shell.h
@@ -0,0 +1,40 @@ +// Copyright 2018 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_EXO_WAYLAND_ZXDG_SHELL_H_ +#define COMPONENTS_EXO_WAYLAND_ZXDG_SHELL_H_ + +#include <stdint.h> + +struct wl_client; + +namespace exo { +class Display; + +namespace wayland { +class SerialTracker; + +struct WaylandZxdgShell { + WaylandZxdgShell(Display* display, SerialTracker* serial_tracker) + : display(display), serial_tracker(serial_tracker) {} + + WaylandZxdgShell(const WaylandZxdgShell&) = delete; + WaylandZxdgShell& operator=(const WaylandZxdgShell&) = delete; + + // Owned by WaylandServerController, which always outlives zxdg_shell. + Display* const display; + + // Owned by Server, which always outlives zxdg_shell. + SerialTracker* const serial_tracker; +}; + +void bind_zxdg_shell_v6(wl_client* client, + void* data, + uint32_t version, + uint32_t id); + +} // namespace wayland +} // namespace exo + +#endif // COMPONENTS_EXO_WAYLAND_ZXDG_SHELL_H_
diff --git a/components/history_clusters/core/config.cc b/components/history_clusters/core/config.cc index 4401db2..51b8d1f 100644 --- a/components/history_clusters/core/config.cc +++ b/components/history_clusters/core/config.cc
@@ -137,14 +137,6 @@ { omnibox_action = base::FeatureList::IsEnabled(internal::kOmniboxAction); - omnibox_action_on_urls = base::GetFieldTrialParamByFeatureAsBool( - internal::kOmniboxAction, "omnibox_action_on_urls", - omnibox_action_on_urls); - - omnibox_action_on_noisy_urls = base::GetFieldTrialParamByFeatureAsBool( - internal::kOmniboxAction, "omnibox_action_on_noisy_urls", - omnibox_action_on_noisy_urls); - omnibox_action_with_pedals = base::GetFieldTrialParamByFeatureAsBool( internal::kOmniboxAction, "omnibox_action_with_pedals", omnibox_action_with_pedals);
diff --git a/components/history_clusters/core/config.h b/components/history_clusters/core/config.h index f05060d..c34abf5 100644 --- a/components/history_clusters/core/config.h +++ b/components/history_clusters/core/config.h
@@ -144,17 +144,6 @@ // for this to take effect. bool omnibox_action = false; - // If enabled, allows the Omnibox Action chip to also appear on URLs. This - // does nothing if `omnibox_action` is disabled. Note, that if you turn this - // flag to true, you almost certainly will want to set - // `omnibox_action_on_navigation_intents` to true as well, as otherwise your - // desired action chips on URLs will almost certainly all be suppressed. - bool omnibox_action_on_urls = false; - - // If enabled, allows the Omnibox Action chip to appear on URLs from noisy - // visits. This does nothing if `omnibox_action_on_urls` is disabled. - bool omnibox_action_on_noisy_urls = true; - // If enabled, allows the Omnibox Action chip to appear when the suggestions // contain pedals. Does nothing if `omnibox_action` is disabled. bool omnibox_action_with_pedals = false;
diff --git a/components/history_clusters/core/history_clusters_service.cc b/components/history_clusters/core/history_clusters_service.cc index d15761f..3bda01d4 100644 --- a/components/history_clusters/core/history_clusters_service.cc +++ b/components/history_clusters/core/history_clusters_service.cc
@@ -388,31 +388,10 @@ return absl::nullopt; } -bool HistoryClustersService::DoesURLMatchAnyCluster( - const std::string& url_keyword) { - if (!IsJourneysEnabled()) - return false; - - // We don't want any omnibox jank for low-end devices. - if (base::SysInfo::IsLowEndDevice()) - return false; - - StartKeywordCacheRefresh(); - if (GetConfig().persist_on_query) - UpdateClusters(); - - return short_url_keywords_cache_.find(url_keyword) != - short_url_keywords_cache_.end() || - all_url_keywords_cache_.find(url_keyword) != - all_url_keywords_cache_.end(); -} - void HistoryClustersService::ClearKeywordCache() { all_keywords_cache_timestamp_ = base::Time(); short_keyword_cache_timestamp_ = base::Time(); all_keywords_cache_.clear(); - all_url_keywords_cache_.clear(); - short_keyword_cache_.clear(); short_keyword_cache_.clear(); cache_keyword_query_task_.reset(); WriteShortCacheToPrefs(); @@ -424,13 +403,11 @@ NotifyDebugMessage("Timestamp: " + GetDebugTime(short_keyword_cache_timestamp_)); NotifyDebugMessage(GetDebugJSONForKeywordMap(short_keyword_cache_)); - NotifyDebugMessage(GetDebugJSONForUrlKeywordSet(short_url_keywords_cache_)); NotifyDebugMessage("-- Printing All-Time Keyword Bag --"); NotifyDebugMessage("Timestamp: " + GetDebugTime(all_keywords_cache_timestamp_)); NotifyDebugMessage(GetDebugJSONForKeywordMap(all_keywords_cache_)); - NotifyDebugMessage(GetDebugJSONForUrlKeywordSet(all_url_keywords_cache_)); NotifyDebugMessage("-- Printing Keyword Bags Done --"); } @@ -469,9 +446,7 @@ base::BindOnce(&HistoryClustersService::PopulateClusterKeywordCache, weak_ptr_factory_.GetWeakPtr(), base::ElapsedTimer(), /*begin_time=*/base::Time(), - std::make_unique<KeywordMap>(), - std::make_unique<URLKeywordSet>(), &all_keywords_cache_, - &all_url_keywords_cache_)); + std::make_unique<KeywordMap>(), &all_keywords_cache_)); } else if ((base::Time::Now() - all_keywords_cache_timestamp_).InSeconds() > 10 && (base::Time::Now() - short_keyword_cache_timestamp_).InSeconds() > @@ -488,9 +463,7 @@ base::BindOnce(&HistoryClustersService::PopulateClusterKeywordCache, weak_ptr_factory_.GetWeakPtr(), base::ElapsedTimer(), all_keywords_cache_timestamp_, - std::make_unique<KeywordMap>(), - std::make_unique<URLKeywordSet>(), &short_keyword_cache_, - &short_url_keywords_cache_)); + std::make_unique<KeywordMap>(), &short_keyword_cache_)); } } @@ -498,9 +471,7 @@ base::ElapsedTimer total_latency_timer, base::Time begin_time, std::unique_ptr<KeywordMap> keyword_accumulator, - std::unique_ptr<URLKeywordSet> url_keyword_accumulator, KeywordMap* cache, - URLKeywordSet* url_cache, std::vector<history::Cluster> clusters, QueryClustersContinuationParams continuation_params) { base::ElapsedThreadTimer populate_keywords_thread_timer; @@ -544,26 +515,6 @@ } } } - - // Push a simplified form of the URL for each visit into the cache. - if (url_keyword_accumulator->size() < max_keyword_phrases) { - for (const auto& visit : cluster.visits) { - if (visit.engagement_score > - GetConfig().noisy_cluster_visits_engagement_threshold && - !GetConfig().omnibox_action_on_noisy_urls) { - // Do not add a noisy visit to the URL keyword accumulator if not - // enabled via flag. Note that this is at the visit-level rather than - // at the cluster-level, which is handled by the NoisyClusterFinalizer - // in the ClusteringBackend. - continue; - } - url_keyword_accumulator->insert( - (!visit.annotated_visit.content_annotations.search_normalized_url - .is_empty()) - ? visit.normalized_url.spec() - : ComputeURLKeywordForLookup(visit.normalized_url)); - } - } } // Make a continuation request to get the next page of clusters and their @@ -572,8 +523,7 @@ constexpr char kKeywordCacheThreadTimeUmaName[] = "History.Clusters.KeywordCache.ThreadTime"; if (!continuation_params.exhausted_all_visits && - (keyword_accumulator->size() < max_keyword_phrases || - url_keyword_accumulator->size() < max_keyword_phrases)) { + keyword_accumulator->size() < max_keyword_phrases) { const ClusteringRequestSource clustering_request_source = cache == &all_keywords_cache_ ? ClusteringRequestSource::kAllKeywordCacheRefresh @@ -586,8 +536,7 @@ weak_ptr_factory_.GetWeakPtr(), std::move(total_latency_timer), begin_time, // Pass on the accumulator sets to the next callback. - std::move(keyword_accumulator), - std::move(url_keyword_accumulator), cache, url_cache)); + std::move(keyword_accumulator), cache)); // Log this even if we go back for more clusters. base::UmaHistogramTimes(kKeywordCacheThreadTimeUmaName, populate_keywords_thread_timer.Elapsed()); @@ -598,12 +547,9 @@ // via the constructor for efficiency (as recommended by the flat_set docs). // De-duplication is handled by the flat_set itself. *cache = std::move(*keyword_accumulator); - *url_cache = std::move(*url_keyword_accumulator); if (ShouldNotifyDebugMessage()) { NotifyDebugMessage("Cache construction complete; keyword cache:"); NotifyDebugMessage(GetDebugJSONForKeywordMap(*cache)); - NotifyDebugMessage("Url cache:"); - NotifyDebugMessage(GetDebugJSONForUrlKeywordSet(*url_cache)); } // Record keyword phrase & keyword counts for the appropriate cache.
diff --git a/components/history_clusters/core/history_clusters_service.h b/components/history_clusters/core/history_clusters_service.h index 06af0fe2..970fe8c 100644 --- a/components/history_clusters/core/history_clusters_service.h +++ b/components/history_clusters/core/history_clusters_service.h
@@ -8,7 +8,6 @@ #include <map> #include <memory> #include <string> -#include <unordered_set> #include <vector> #include "base/functional/callback.h" @@ -85,7 +84,6 @@ // percentile, and we do synchronous lookups as the user types in the omnibox. using KeywordMap = std::unordered_map<std::u16string, history::ClusterKeywordData>; - using URLKeywordSet = std::unordered_set<std::string>; // `url_loader_factory` is allowed to be nullptr, like in unit tests. HistoryClustersService( @@ -186,12 +184,6 @@ absl::optional<history::ClusterKeywordData> DoesQueryMatchAnyCluster( const std::string& query); - // Returns true if `url_keyword` matches a URL in a significant cluster. This - // may kick off a cache refresh while still immediately returning false. - // `url_keyword` is derived from a given URL by ComputeURLKeywordForLookup(). - // SRP URLs canonicalized by TemplateURLService should be passed in directly. - bool DoesURLMatchAnyCluster(const std::string& url_keyword); - // Clears `all_keywords_cache_` and cancels any pending tasks to populate it. void ClearKeywordCache(); @@ -219,9 +211,7 @@ base::ElapsedTimer total_latency_timer, base::Time begin_time, std::unique_ptr<KeywordMap> keyword_accumulator, - std::unique_ptr<URLKeywordSet> url_keyword_accumulator, KeywordMap* cache, - URLKeywordSet* url_cache, std::vector<history::Cluster> clusters, QueryClustersContinuationParams continuation_params); @@ -255,7 +245,6 @@ // the cache was generated so we can periodically re-generate. // TODO(tommycli): Make a smarter mechanism for regenerating the cache. KeywordMap all_keywords_cache_; - URLKeywordSet all_url_keywords_cache_; base::Time all_keywords_cache_timestamp_; // Like above, but will represent the clusters newer than @@ -269,7 +258,6 @@ // TODO(manukh) This is a "band aid" fix to missing keywords for recent // visits. KeywordMap short_keyword_cache_; - URLKeywordSet short_url_keywords_cache_; base::Time short_keyword_cache_timestamp_; // Tracks the current keyword task. Will be `nullptr` or
diff --git a/components/history_clusters/core/history_clusters_service_test_api.h b/components/history_clusters/core/history_clusters_service_test_api.h index c685734a..860b217 100644 --- a/components/history_clusters/core/history_clusters_service_test_api.h +++ b/components/history_clusters/core/history_clusters_service_test_api.h
@@ -66,10 +66,6 @@ history_clusters_service_->all_keywords_cache_ = cache; } - void SetAllUrlKeywordsCache(HistoryClustersService::URLKeywordSet cache) { - history_clusters_service_->all_url_keywords_cache_ = cache; - } - HistoryClustersService* const history_clusters_service_; history::HistoryService* const history_service_; };
diff --git a/components/history_clusters/core/history_clusters_service_unittest.cc b/components/history_clusters/core/history_clusters_service_unittest.cc index d9b1448..a66335d 100644 --- a/components/history_clusters/core/history_clusters_service_unittest.cc +++ b/components/history_clusters/core/history_clusters_service_unittest.cc
@@ -1390,280 +1390,6 @@ ExpectSyncedVisits()); } -TEST_P(HistoryClustersServiceTest, DoesURLMatchAnyClusterWithNoisyURLs) { - Config config; - config.omnibox_action_on_urls = true; - config.omnibox_action_on_noisy_urls = true; - SetConfigForTesting(config); - - AddHardcodedTestDataToHistoryService(); - - // Verify that initially, the test URL doesn't match anything, but this - // query should have kicked off a cache population request. This is the URL - // for visit 5. - EXPECT_FALSE(history_clusters_service_->DoesURLMatchAnyCluster( - ComputeURLKeywordForLookup(GURL("https://second-1-day-old-visit.com/")))); - - std::vector<history::Cluster> clusters; - // This cluster should contribute to keywords. - clusters.push_back(history::Cluster( - 0, - { - GetHardcodedClusterVisit(5), - GetHardcodedClusterVisit(5), - GetHardcodedClusterVisit( - /*visit_id=*/2, /*score=*/0.0, /*engagement_score=*/20.0), - }, - {{u"apples", history::ClusterKeywordData()}, - {u"oranges", history::ClusterKeywordData()}, - {u"z", history::ClusterKeywordData()}, - {u"apples bananas", history::ClusterKeywordData()}}, - /*should_show_on_prominent_ui_surfaces=*/true)); - // This cluster should NOT contribute to keywords because - // `should_show_on_prominent_ui_surfaces` is false. - clusters.push_back( - history::Cluster(0, - { - GetHardcodedClusterVisit(5), - GetHardcodedClusterVisit(2), - }, - {{u"sensitive", history::ClusterKeywordData()}}, - /*should_show_on_prominent_ui_surfaces=*/false)); - // This cluster should NOT contribute to keywords because it only has 1 - // visible visit. - clusters.push_back( - history::Cluster(0, - { - GetHardcodedClusterVisit(2), - GetHardcodedClusterVisit(2, /*score=*/0), - }, - {{u"singlevisit", history::ClusterKeywordData()}}, - /*should_show_on_prominent_ui_surfaces=*/true)); - - // Hardcoded test visits span 3 days (1-day-old, 2-days-old, and 60-day-old). - FlushKeywordRequests(clusters, 3); - - // Now the exact query should match the populated cache. - EXPECT_TRUE(history_clusters_service_->DoesURLMatchAnyCluster( - ComputeURLKeywordForLookup(GURL("https://second-1-day-old-visit.com/")))); - - // Github should be shown since we are including visits from noisy URLs. - EXPECT_TRUE(history_clusters_service_->DoesURLMatchAnyCluster( - ComputeURLKeywordForLookup(GURL("https://github.com/")))); - - // Deleting a history entry should clear the keyword cache. - history_service_->DeleteURLs({GURL{"https://google.com/"}}); - history::BlockUntilHistoryProcessesPendingRequests(history_service_.get()); - EXPECT_FALSE(history_clusters_service_->DoesURLMatchAnyCluster( - ComputeURLKeywordForLookup(GURL("https://second-1-day-old-visit.com/")))); - - // Visits now span 2 days (1-day-old and 60-day-old) since we deleted the only - // 2-day-old visit. - FlushKeywordRequests(clusters, 2); - - // The keyword cache should be repopulated. - EXPECT_TRUE(history_clusters_service_->DoesURLMatchAnyCluster( - ComputeURLKeywordForLookup(GURL("https://second-1-day-old-visit.com/")))); -} - -TEST_P(HistoryClustersServiceTest, DoesURLMatchAnyClusterNoNoisyURLs) { - Config config; - config.omnibox_action_on_urls = true; - config.omnibox_action_on_noisy_urls = false; - SetConfigForTesting(config); - - AddHardcodedTestDataToHistoryService(); - - // Verify that initially, the test URL doesn't match anything, but this - // query should have kicked off a cache population request. This is the URL - // for visit 5. - EXPECT_FALSE(history_clusters_service_->DoesURLMatchAnyCluster( - ComputeURLKeywordForLookup(GURL("https://second-1-day-old-visit.com/")))); - - std::vector<history::Cluster> clusters; - // This cluster should contribute to keywords. - clusters.push_back(history::Cluster( - 0, - { - GetHardcodedClusterVisit(5), - GetHardcodedClusterVisit(5), - GetHardcodedClusterVisit( - /*visit_id=*/2, /*score=*/0.0, /*engagement_score=*/20.0), - }, - {{u"apples", history::ClusterKeywordData()}, - {u"oranges", history::ClusterKeywordData()}, - {u"z", history::ClusterKeywordData()}, - {u"apples bananas", history::ClusterKeywordData()}}, - /*should_show_on_prominent_ui_surfaces=*/true)); - // This cluster should NOT contribute to keywords because - // `should_show_on_prominent_ui_surfaces` is false. - clusters.push_back( - history::Cluster(0, - { - GetHardcodedClusterVisit(5), - GetHardcodedClusterVisit(2), - }, - {{u"sensitive", history::ClusterKeywordData()}}, - /*should_show_on_prominent_ui_surfaces=*/false)); - // This cluster should NOT contribute to keywords because it only has 1 - // visible visit. - clusters.push_back( - history::Cluster(0, - { - GetHardcodedClusterVisit(2), - GetHardcodedClusterVisit(2, /*score=*/0), - }, - {{u"singlevisit", history::ClusterKeywordData()}}, - /*should_show_on_prominent_ui_surfaces=*/true)); - - // Hardcoded test visits span 3 days (1-day-old, 2-days-old, and 60-day-old). - FlushKeywordRequests(clusters, 3); - - // Now the exact query should match the populated cache. - EXPECT_TRUE(history_clusters_service_->DoesURLMatchAnyCluster( - ComputeURLKeywordForLookup(GURL("https://second-1-day-old-visit.com/")))); - - // Github should never be shown (highly-engaged for cluster 1, sensitive for - // cluster 2, single visit cluster for cluster 3). - EXPECT_FALSE(history_clusters_service_->DoesURLMatchAnyCluster( - ComputeURLKeywordForLookup(GURL("https://github.com/")))); - - // Deleting a history entry should clear the keyword cache. - history_service_->DeleteURLs({GURL{"https://google.com/"}}); - history::BlockUntilHistoryProcessesPendingRequests(history_service_.get()); - EXPECT_FALSE(history_clusters_service_->DoesURLMatchAnyCluster( - ComputeURLKeywordForLookup(GURL("https://second-1-day-old-visit.com/")))); - - // Visits now span 2 days (1-day-old and 60-day-old) since we deleted the only - // 2-day-old visit. - FlushKeywordRequests(clusters, 2); - - // The keyword cache should be repopulated. - EXPECT_TRUE(history_clusters_service_->DoesURLMatchAnyCluster( - ComputeURLKeywordForLookup(GURL("https://second-1-day-old-visit.com/")))); -} - -class HistoryClustersServicePrefPersistenceTest - : public HistoryClustersServiceTestBase { - public: - HistoryClustersServicePrefPersistenceTest() { - scoped_feature_list_.InitWithFeatures( - /*enabled_features=*/{internal::kJourneys, - internal::kJourneysPersistCachesToPrefs}, - /*disabled_features=*/{}); - Config config; - config.persist_clusters_in_history_db = true; - SetConfigForTesting(config); - } -}; - -TEST_F(HistoryClustersServicePrefPersistenceTest, LoadCachesFromPrefs) { - AddHardcodedTestDataToHistoryService(); - EXPECT_FALSE(history_clusters_service_->DoesQueryMatchAnyCluster("apples")); - - std::vector<history::Cluster> clusters; - clusters.push_back(history::Cluster( - 0, - { - GetHardcodedClusterVisit(5), - GetHardcodedClusterVisit(2), - }, - {{u"apples", - history::ClusterKeywordData(history::ClusterKeywordData::kEntity, 5.0f, - {"fuji", "honeycrisp"})}, - {u"oranges", history::ClusterKeywordData( - history::ClusterKeywordData::kSearchTerms, 100.0f, {})}, - {u"z", history::ClusterKeywordData()}, - {u"apples bananas", history::ClusterKeywordData()}}, - /*should_show_on_prominent_ui_surfaces=*/true)); - - FlushKeywordRequests(clusters, 3); - - const auto keyword_data = - history_clusters_service_->DoesQueryMatchAnyCluster("apples"); - EXPECT_TRUE(keyword_data); - - // Empty the cache artificially to simulate a process restart. - history_clusters_service_test_api_->SetAllKeywordsCache({}); - EXPECT_FALSE(history_clusters_service_->DoesQueryMatchAnyCluster("apples")); - - LoadCachesFromPrefs(); - - const auto apples_keyword_data = - history_clusters_service_->DoesQueryMatchAnyCluster("apples"); - EXPECT_TRUE(apples_keyword_data); - EXPECT_EQ(apples_keyword_data, - history::ClusterKeywordData(history::ClusterKeywordData::kEntity, - 5.0f, {"fuji", "honeycrisp"})); - const auto oranges_keyword_data = - history_clusters_service_->DoesQueryMatchAnyCluster("oranges"); - EXPECT_TRUE(oranges_keyword_data); - EXPECT_EQ(oranges_keyword_data, - history::ClusterKeywordData(history::ClusterKeywordData( - history::ClusterKeywordData::kSearchTerms, 100.0f, {}))); - EXPECT_TRUE( - history_clusters_service_->DoesQueryMatchAnyCluster("apples bananas")); -} - -TEST_F(HistoryClustersServicePrefPersistenceTest, - LoadSecondaryCachesFromPrefs) { - AddHardcodedTestDataToHistoryService(); - auto minutes_ago = [](int minutes) { - return base::Time::Now() - base::Minutes(minutes); - }; - - // Set up the cache timestamps. - history_clusters_service_test_api_->SetAllKeywordsCacheTimestamp( - minutes_ago(60)); - history_clusters_service_test_api_->SetShortKeywordCacheTimestamp( - minutes_ago(15)); - - // Set up the visit timestamps. - // Visits newer than both cache timestamps should be reclustered. - auto visit = GetHardcodedTestVisits()[0]; - visit.visit_row.visit_time = minutes_ago(5); - AddCompleteVisit(visit); - visit = GetHardcodedTestVisits()[1]; - visit.visit_row.visit_time = minutes_ago(10); - AddCompleteVisit(visit); - - // Kick off cluster request and verify the correct visits are sent. - EXPECT_FALSE(history_clusters_service_->DoesQueryMatchAnyCluster("peach")); - test_clustering_backend_->WaitForGetClustersCall(); - - // Send the cluster response and verify the keyword was cached. - std::vector<history::Cluster> clusters2; - clusters2.push_back(history::Cluster( - 0, - { - GetHardcodedClusterVisit(1), - GetHardcodedClusterVisit(2), - }, - {{u"peach", - history::ClusterKeywordData(history::ClusterKeywordData::kEntity, 13.0f, - {"georgia"})}, - {u"", history::ClusterKeywordData()}}, - /*should_show_on_prominent_ui_surfaces=*/true)); - test_clustering_backend_->FulfillCallback(clusters2); - history::BlockUntilHistoryProcessesPendingRequests(history_service_.get()); - EXPECT_TRUE(history_clusters_service_->DoesQueryMatchAnyCluster("peach")); - - // Verify the keyword is in the short cache specifically. - history_clusters_service_test_api_->SetAllKeywordsCache({}); - EXPECT_TRUE(history_clusters_service_->DoesQueryMatchAnyCluster("peach")); - - // Empty the cache artificially to simulate a process restart. - history_clusters_service_test_api_->SetShortKeywordCache({}); - EXPECT_FALSE(history_clusters_service_->DoesQueryMatchAnyCluster("peach")); - - LoadCachesFromPrefs(); - const auto peach_keyword_data = - history_clusters_service_->DoesQueryMatchAnyCluster("peach"); - EXPECT_EQ(peach_keyword_data, - history::ClusterKeywordData(history::ClusterKeywordData( - history::ClusterKeywordData::kEntity, 13.0f, {"georgia"}))); -} - class HistoryClustersServiceJourneysDisabledTest : public HistoryClustersServiceTestBase { public:
diff --git a/components/history_clusters/core/history_clusters_util.cc b/components/history_clusters/core/history_clusters_util.cc index 539b9fbc..561835e7 100644 --- a/components/history_clusters/core/history_clusters_util.cc +++ b/components/history_clusters/core/history_clusters_util.cc
@@ -14,7 +14,6 @@ #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "components/history/core/browser/history_types.h" -#include "components/history/core/browser/visitsegment_database.h" #include "components/history_clusters/core/config.h" #include "components/history_clusters/core/features.h" #include "components/history_clusters/core/history_clusters_types.h" @@ -167,11 +166,6 @@ return url_for_deduping; } -std::string ComputeURLKeywordForLookup(const GURL& url) { - return history::VisitSegmentDatabase::ComputeSegmentName( - ComputeURLForDeduping(url)); -} - std::u16string ComputeURLForDisplay(const GURL& url, bool trim_after_host) { // Use URL formatting options similar to the omnibox popup. The url_formatter // component does IDN hostname conversion as well.
diff --git a/components/history_clusters/core/history_clusters_util.h b/components/history_clusters/core/history_clusters_util.h index d14fbe3f..c0d8071 100644 --- a/components/history_clusters/core/history_clusters_util.h +++ b/components/history_clusters/core/history_clusters_util.h
@@ -23,15 +23,6 @@ // should be separately canonicalized by TemplateURLService and not sent here. GURL ComputeURLForDeduping(const GURL& url); -// Generates a keyword from the URL used for looking up relevant clusters to a -// given URL. Does everything that ComputeURLForDeduping() does and additionally -// applies history::VisitSegmentDatabase::ComputeSegmentName() to the resulting -// URL to maximize coverage. -// -// Note, this is NOT meant to be applied to Search Result Page URLs. Those -// should be separately canonicalized by TemplateURLService and not sent here. -std::string ComputeURLKeywordForLookup(const GURL& url); - // Returns a string suitable for display in the Journeys UI from the normalized // visit URL. Displays the host and the path. Set `trim_after_host` to true to // also remove the path, query, and ref.
diff --git a/components/history_clusters/core/history_clusters_util_unittest.cc b/components/history_clusters/core/history_clusters_util_unittest.cc index 1ebdb3c..3ac30fc5 100644 --- a/components/history_clusters/core/history_clusters_util_unittest.cc +++ b/components/history_clusters/core/history_clusters_util_unittest.cc
@@ -55,47 +55,6 @@ } } -TEST(HistoryClustersUtilTest, ComputeURLKeywordForLookup) { - { - Config config; - config.use_host_for_visit_deduping = false; - SetConfigForTesting(config); - - EXPECT_EQ(ComputeURLKeywordForLookup(GURL("http://www.google.com/")), - "http://google.com/") - << "Strip off WWW."; - EXPECT_EQ(ComputeURLKeywordForLookup(GURL("https://google.com/")), - "http://google.com/") - << "Normalizes scheme to http."; - EXPECT_EQ(ComputeURLKeywordForLookup( - GURL("http://google.com/path?foo=bar#reftag")), - "http://google.com/path") - << "Strips ref and query, leaves path."; - EXPECT_EQ(ComputeURLKeywordForLookup( - GURL("https://www.google.com/path?foo=bar#reftag")), - "http://google.com/path") - << "Does all of the above at once."; - EXPECT_EQ(ComputeURLKeywordForLookup(GURL("http://google.com/path")), - "http://google.com/path") - << "Sanity check when no replacements needed."; - } - - { - Config config; - config.use_host_for_visit_deduping = true; - SetConfigForTesting(config); - - EXPECT_EQ(ComputeURLKeywordForLookup(GURL("https://google.com/path/")), - "http://google.com/") - << "Strips path."; - - EXPECT_EQ(ComputeURLKeywordForLookup( - GURL("https://www.google.com/path?foo=bar#reftag")), - "http://google.com/") - << "Does everything at once."; - } -} - TEST(HistoryClustersUtilTest, FilterClustersMatchingQuery) { std::vector<history::Cluster> all_clusters; all_clusters.push_back(
diff --git a/components/omnibox/browser/BUILD.gn b/components/omnibox/browser/BUILD.gn index 47c00e4..10fec13 100644 --- a/components/omnibox/browser/BUILD.gn +++ b/components/omnibox/browser/BUILD.gn
@@ -51,6 +51,7 @@ "extension_app.icon", "find_in_page.icon", "http.icon", + "http_chrome_refresh.icon", "https_valid_in_chip.icon", "incognito.icon", "install_desktop.icon", @@ -62,9 +63,12 @@ "price_tracking_disabled.icon", "price_tracking_enabled_filled.icon", "product.icon", + "product_chrome_refresh.icon", "share.icon", "star.icon", "star_active.icon", + "star_active_chrome_refresh.icon", + "star_chrome_refresh.icon", "switch.icon", "tab.icon", "trending_up.icon",
diff --git a/components/omnibox/browser/actions/history_clusters_action.cc b/components/omnibox/browser/actions/history_clusters_action.cc index 5523153..7fc36a57 100644 --- a/components/omnibox/browser/actions/history_clusters_action.cc +++ b/components/omnibox/browser/actions/history_clusters_action.cc
@@ -258,17 +258,6 @@ query, std::move(matched_keyword_data.value()), /*takes_over_match=*/false)); } - } else if (GetConfig().omnibox_action_on_urls) { - // We do the URL stripping here, because we need it to both execute the - // query, as well as to feed it into the action chip so the chip navigates - // to the right place (with the query pre-populated). - std::string url_keyword = - history_clusters::ComputeURLKeywordForLookup(match.destination_url); - if (service->DoesURLMatchAnyCluster(url_keyword)) { - match.actions.push_back(base::MakeRefCounted<HistoryClustersAction>( - url_keyword, history::ClusterKeywordData(), - /*takes_over_match=*/false)); - } } // Only ever attach one action (to the highest match), to not overwhelm
diff --git a/components/omnibox/browser/actions/history_clusters_action_unittest.cc b/components/omnibox/browser/actions/history_clusters_action_unittest.cc index aea6f37c..164c5b5b 100644 --- a/components/omnibox/browser/actions/history_clusters_action_unittest.cc +++ b/components/omnibox/browser/actions/history_clusters_action_unittest.cc
@@ -70,14 +70,7 @@ search_actions_config_.is_journeys_enabled_no_locale_check = true; search_actions_config_.omnibox_action = true; search_actions_config_.omnibox_action_on_navigation_intents = false; - search_actions_config_.omnibox_action_on_urls = false; SetConfigForTesting(search_actions_config_); - - url_actions_config_.is_journeys_enabled_no_locale_check = true; - url_actions_config_.omnibox_action = true; - url_actions_config_.omnibox_action_on_navigation_intents = false; - url_actions_config_.omnibox_action_on_urls = true; - SetConfigForTesting(url_actions_config_); } // `history_clusters_service_` needs to be initialized repeatedly since it @@ -98,8 +91,6 @@ history_clusters_service_.get(), history_service_.get()); history_clusters_service_test_api_->SetAllKeywordsCache( {{u"keyword", history::ClusterKeywordData()}}); - history_clusters_service_test_api_->SetAllUrlKeywordsCache( - {"http://keyword/"}); } void TestAttachHistoryClustersActions(std::vector<MatchData> matches_data, @@ -141,14 +132,12 @@ // Commonly used configs & prefs used or derived from in the tests. Config search_actions_config_; - Config url_actions_config_; TestingPrefServiceSimple prefs_enabled_; }; TEST_F(HistoryClustersActionTest, AttachHistoryClustersActions) { { SCOPED_TRACE("Shouldn't add action if history cluster service is nullptr."); - SetUpWithConfig(search_actions_config_); TestAttachHistoryClustersActions({{}}, nullptr, &prefs_enabled_); } @@ -217,19 +206,7 @@ } { - SCOPED_TRACE( - "Should add action if a navigation suggestion matches and " - "`omnibox_action_on_urls` is enabled."); - SetUpWithConfig(url_actions_config_); - TestAttachHistoryClustersActions( - {{.type = AutocompleteMatchType::Type::HISTORY_TITLE, - .expect_history_clusters_action = true}}); - } - - { - SCOPED_TRACE( - "Should not add action if a navigation suggestion matches and " - "`omnibox_action_on_urls` is disabled."); + SCOPED_TRACE("Should not add action if a navigation suggestion matches."); SetUpWithConfig(search_actions_config_); TestAttachHistoryClustersActions( {{.type = AutocompleteMatchType::Type::HISTORY_TITLE}}); @@ -238,8 +215,8 @@ { SCOPED_TRACE( "Should add action if both a search and navigation suggestions " - "match and `omnibox_action_on_urls` is disabled. The search suggestion " - "should have an action, even if it is ranked & scored lower."); + "match. The search suggestion should have an action, even if it is " + "ranked & scored lower."); SetUpWithConfig(search_actions_config_); TestAttachHistoryClustersActions( {{.type = AutocompleteMatchType::Type::HISTORY_TITLE}, @@ -273,16 +250,6 @@ { SCOPED_TRACE( - "Should add action to a top-scoring navigation suggestion, if it is " - "not high-scoring."); - SetUpWithConfig(url_actions_config_); - TestAttachHistoryClustersActions( - {{.type = AutocompleteMatchType::Type::HISTORY_TITLE, - .expect_history_clusters_action = true}}); - } - - { - SCOPED_TRACE( "Should not add action if a search suggestion matches and the top " "scoring suggestion is a high score navigation suggestion, even if it " "doesn't match."); @@ -307,20 +274,6 @@ .type = AutocompleteMatchType::Type::HISTORY_TITLE}, {.relevance = 1350, .expect_history_clusters_action = true}}); } - - { - SCOPED_TRACE( - "Should add action to a top scoring, high score navigation " - "suggestion if `omnibox_action_on_navigation_intents` is enabled."); - Config config = url_actions_config_; - config.omnibox_action_on_navigation_intents = true; - SetUpWithConfig(config); - TestAttachHistoryClustersActions({ - {.relevance = 1350, - .type = AutocompleteMatchType::Type::HISTORY_TITLE, - .expect_history_clusters_action = true}, - }); - } } } // namespace history_clusters
diff --git a/components/omnibox/browser/autocomplete_grouper_sections.cc b/components/omnibox/browser/autocomplete_grouper_sections.cc index b633b74..0885780b 100644 --- a/components/omnibox/browser/autocomplete_grouper_sections.cc +++ b/components/omnibox/browser/autocomplete_grouper_sections.cc
@@ -109,6 +109,7 @@ : ZpsSection( 15 + max_related_queries + max_trending_queries, { + {1, omnibox::GROUP_MOBILE_CLIPBOARD}, {15, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST}, {max_related_queries, omnibox::GROUP_PREVIOUS_SEARCH_RELATED}, {max_trending_queries, omnibox::GROUP_TRENDS},
diff --git a/components/omnibox/browser/autocomplete_grouper_sections_unittest.cc b/components/omnibox/browser/autocomplete_grouper_sections_unittest.cc index 4289bf6b..53a5d8c 100644 --- a/components/omnibox/browser/autocomplete_grouper_sections_unittest.cc +++ b/components/omnibox/browser/autocomplete_grouper_sections_unittest.cc
@@ -926,7 +926,7 @@ } // Tests the groups, limits, and rules for the Android NTP ZPS+Inspire Me. -TEST(AutocompleteGrouperSectionsTest, AndroidNTPZpsSection) { +TEST(AutocompleteGrouperSectionsTest, AndroidNTPZpsSection_withInspireMe) { auto test = [](ACMatches matches, std::vector<int> expected_relevances) { constexpr int MAX_PREVIOUS_SEARCH_RELATED = 3; constexpr int MAX_TRENDING_QUERIES = 5; @@ -948,10 +948,6 @@ // Verify that the Clipboard suggestion is retained on top. test( { - // Auxiliary matches not valid for NTP ZPS. - CreateMatch(200, omnibox::GROUP_MOBILE_CLIPBOARD), - CreateMatch(199, omnibox::GROUP_MOBILE_SEARCH_READY_OMNIBOX), - CreateMatch(198, omnibox::GROUP_MOBILE_MOST_VISITED), // PSUGGEST to show on the NTP ZPS. CreateMatch(100, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), CreateMatch(99, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), @@ -974,6 +970,35 @@ {100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86}); } { + SCOPED_TRACE("Clipboard suggestion is always shown when available."); + test( + { + CreateMatch(3, omnibox::GROUP_MOBILE_CLIPBOARD), + // Auxiliary matches not valid for NTP ZPS. + CreateMatch(2, omnibox::GROUP_MOBILE_SEARCH_READY_OMNIBOX), + CreateMatch(1, omnibox::GROUP_MOBILE_MOST_VISITED), + // PSUGGEST to show on the NTP ZPS. + CreateMatch(100, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(99, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(98, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(97, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(96, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(95, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(94, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(93, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(92, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(91, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(90, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(89, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(88, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(87, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(86, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(85, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(84, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + }, + {3, 100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86}); + } + { SCOPED_TRACE("No Trending Queries Backfill"); // Verify that Trending queries don't backfill unoccupied Related queries // slots. @@ -1040,3 +1065,92 @@ {19, 20}); } } + +TEST(AutocompleteGrouperSectionsTest, AndroidNTPZpsSection_noInspireMe) { + auto test = [](ACMatches matches, std::vector<int> expected_relevances) { + PSections sections; + omnibox::GroupConfigMap group_configs; + sections.push_back( + std::make_unique<AndroidNTPZpsSection>(0, 0, group_configs)); + auto out_matches = Section::GroupMatches(std::move(sections), matches); + VerifyMatches(out_matches, expected_relevances); + }; + + { + SCOPED_TRACE("Given no matches, should return no matches."); + test({}, {}); + } + { + SCOPED_TRACE("Given no InspireMe matches, should return no matches."); + // Verify that the Clipboard suggestion is retained on top. + test( + { + // PSUGGEST to show on the NTP ZPS. + CreateMatch(100, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(99, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(98, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(97, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(96, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(95, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(94, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(93, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(92, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(91, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(90, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(89, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(88, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(87, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(86, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(85, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(84, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + }, + {100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86}); + } + { + SCOPED_TRACE("Clipboard suggestion is always shown when available."); + test( + { + CreateMatch(3, omnibox::GROUP_MOBILE_CLIPBOARD), + // Auxiliary matches not valid for NTP ZPS. + CreateMatch(2, omnibox::GROUP_MOBILE_SEARCH_READY_OMNIBOX), + CreateMatch(1, omnibox::GROUP_MOBILE_MOST_VISITED), + // PSUGGEST to show on the NTP ZPS. + CreateMatch(100, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(99, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(98, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(97, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(96, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(95, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(94, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(93, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(92, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(91, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(90, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(89, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(88, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(87, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(86, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(85, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(84, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + }, + {3, 100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87}); + } + { + SCOPED_TRACE("No Trending or Related searches"); + test( + { + CreateMatch(20, omnibox::GROUP_PREVIOUS_SEARCH_RELATED), + CreateMatch(19, omnibox::GROUP_PREVIOUS_SEARCH_RELATED), + CreateMatch(18, omnibox::GROUP_PREVIOUS_SEARCH_RELATED), + CreateMatch(17, omnibox::GROUP_PREVIOUS_SEARCH_RELATED), + CreateMatch(16, omnibox::GROUP_PREVIOUS_SEARCH_RELATED), + CreateMatch(15, omnibox::GROUP_TRENDS), + CreateMatch(14, omnibox::GROUP_TRENDS), + CreateMatch(13, omnibox::GROUP_TRENDS), + CreateMatch(12, omnibox::GROUP_TRENDS), + CreateMatch(11, omnibox::GROUP_TRENDS), + CreateMatch(10, omnibox::GROUP_TRENDS), + }, + {}); + } +}
diff --git a/components/omnibox/browser/location_bar_model_util.cc b/components/omnibox/browser/location_bar_model_util.cc index c3f5ab4a..165ac0c 100644 --- a/components/omnibox/browser/location_bar_model_util.cc +++ b/components/omnibox/browser/location_bar_model_util.cc
@@ -9,6 +9,7 @@ #include "build/build_config.h" #include "components/omnibox/browser/buildflags.h" #include "components/omnibox/common/omnibox_features.h" +#include "ui/base/ui_base_features.h" #include "ui/gfx/vector_icon_types.h" #if (!BUILDFLAG(IS_ANDROID) || BUILDFLAG(ENABLE_VR)) && !BUILDFLAG(IS_IOS) @@ -24,23 +25,30 @@ #if (!BUILDFLAG(IS_ANDROID) || BUILDFLAG(ENABLE_VR)) && !BUILDFLAG(IS_IOS) switch (security_level) { case security_state::NONE: - return omnibox::kHttpIcon; + return features::IsChromeRefresh2023() ? omnibox::kHttpChromeRefreshIcon + : omnibox::kHttpIcon; case security_state::SECURE: { return use_updated_connection_security_indicators ? vector_icons::kHttpsValidArrowIcon : vector_icons::kHttpsValidIcon; } case security_state::SECURE_WITH_POLICY_INSTALLED_CERT: - return vector_icons::kBusinessIcon; + return features::IsChromeRefresh2023() + ? vector_icons::kBusinessChromeRefreshIcon + : vector_icons::kBusinessIcon; case security_state::WARNING: case security_state::DANGEROUS: - return vector_icons::kNotSecureWarningIcon; + return features::IsChromeRefresh2023() + ? vector_icons::kNotSecureWarningChromeRefreshIcon + : vector_icons::kNotSecureWarningIcon; case security_state::SECURITY_LEVEL_COUNT: NOTREACHED(); - return omnibox::kHttpIcon; + return features::IsChromeRefresh2023() ? omnibox::kHttpChromeRefreshIcon + : omnibox::kHttpIcon; } NOTREACHED(); - return omnibox::kHttpIcon; + return features::IsChromeRefresh2023() ? omnibox::kHttpChromeRefreshIcon + : omnibox::kHttpIcon; #else NOTREACHED(); static const gfx::VectorIcon dummy = {};
diff --git a/components/omnibox/browser/vector_icons/http_chrome_refresh.icon b/components/omnibox/browser/vector_icons/http_chrome_refresh.icon new file mode 100644 index 0000000..5a01699 --- /dev/null +++ b/components/omnibox/browser/vector_icons/http_chrome_refresh.icon
@@ -0,0 +1,127 @@ +// 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. + +CANVAS_DIMENSIONS, 32, +MOVE_TO, 14.8f, 22.67f, +H_LINE_TO, 17.27f, +V_LINE_TO, 14.67f, +H_LINE_TO, 14.8f, +V_LINE_TO, 22.67f, +CLOSE, +MOVE_TO, 16, 12.07f, +CUBIC_TO, 16.36f, 12.07f, 16.66f, 11.96f, 16.9f, 11.73f, +CUBIC_TO, 17.14f, 11.49f, 17.27f, 11.19f, 17.27f, 10.83f, +CUBIC_TO, 17.27f, 10.48f, 17.14f, 10.18f, 16.9f, 9.93f, +CUBIC_TO, 16.66f, 9.69f, 16.36f, 9.57f, 16, 9.57f, +CUBIC_TO, 15.64f, 9.57f, 15.34f, 9.69f, 15.1f, 9.93f, +CUBIC_TO, 14.86f, 10.18f, 14.73f, 10.48f, 14.73f, 10.83f, +CUBIC_TO, 14.73f, 11.19f, 14.86f, 11.49f, 15.1f, 11.73f, +CUBIC_TO, 15.34f, 11.96f, 15.64f, 12.07f, 16, 12.07f, +CLOSE, +MOVE_TO, 16, 29.33f, +CUBIC_TO, 14.16f, 29.33f, 12.42f, 28.99f, 10.8f, 28.3f, +CUBIC_TO, 9.18f, 27.59f, 7.77f, 26.63f, 6.57f, 25.43f, +CUBIC_TO, 5.37f, 24.23f, 4.41f, 22.82f, 3.7f, 21.2f, +CUBIC_TO, 3.01f, 19.58f, 2.67f, 17.84f, 2.67f, 16, +CUBIC_TO, 2.67f, 14.16f, 3.01f, 12.42f, 3.7f, 10.8f, +CUBIC_TO, 4.41f, 9.18f, 5.37f, 7.77f, 6.57f, 6.57f, +CUBIC_TO, 7.77f, 5.37f, 9.18f, 4.42f, 10.8f, 3.73f, +CUBIC_TO, 12.42f, 3.02f, 14.16f, 2.67f, 16, 2.67f, +CUBIC_TO, 17.84f, 2.67f, 19.58f, 3.02f, 21.2f, 3.73f, +CUBIC_TO, 22.82f, 4.42f, 24.23f, 5.37f, 25.43f, 6.57f, +CUBIC_TO, 26.63f, 7.77f, 27.58f, 9.18f, 28.27f, 10.8f, +CUBIC_TO, 28.98f, 12.42f, 29.33f, 14.16f, 29.33f, 16, +CUBIC_TO, 29.33f, 17.84f, 28.98f, 19.58f, 28.27f, 21.2f, +CUBIC_TO, 27.58f, 22.82f, 26.63f, 24.23f, 25.43f, 25.43f, +CUBIC_TO, 24.23f, 26.63f, 22.82f, 27.59f, 21.2f, 28.3f, +CUBIC_TO, 19.58f, 28.99f, 17.84f, 29.33f, 16, 29.33f, +CLOSE, +MOVE_TO, 16, 26.9f, +CUBIC_TO, 19.04f, 26.9f, 21.62f, 25.84f, 23.73f, 23.73f, +CUBIC_TO, 25.84f, 21.6f, 26.9f, 19.02f, 26.9f, 16, +CUBIC_TO, 26.9f, 12.96f, 25.84f, 10.38f, 23.73f, 8.27f, +CUBIC_TO, 21.62f, 6.16f, 19.04f, 5.1f, 16, 5.1f, +CUBIC_TO, 12.98f, 5.1f, 10.4f, 6.16f, 8.27f, 8.27f, +CUBIC_TO, 6.16f, 10.38f, 5.1f, 12.96f, 5.1f, 16, +CUBIC_TO, 5.1f, 19.02f, 6.16f, 21.6f, 8.27f, 23.73f, +CUBIC_TO, 10.4f, 25.84f, 12.98f, 26.9f, 16, 26.9f, +CLOSE, +NEW_PATH + +CANVAS_DIMENSIONS, 20, +MOVE_TO, 9.25f, 14, +H_LINE_TO, 10.75f, +V_LINE_TO, 9, +H_LINE_TO, 9.25f, +V_LINE_TO, 14, +CLOSE, +MOVE_TO, 10, 7.5f, +CUBIC_TO, 10.21f, 7.5f, 10.38f, 7.43f, 10.52f, 7.29f, +CUBIC_TO, 10.67f, 7.14f, 10.75f, 6.96f, 10.75f, 6.75f, +CUBIC_TO, 10.75f, 6.54f, 10.67f, 6.37f, 10.52f, 6.23f, +CUBIC_TO, 10.38f, 6.08f, 10.21f, 6, 10, 6, +CUBIC_TO, 9.79f, 6, 9.61f, 6.08f, 9.46f, 6.23f, +CUBIC_TO, 9.32f, 6.37f, 9.25f, 6.54f, 9.25f, 6.75f, +CUBIC_TO, 9.25f, 6.96f, 9.32f, 7.14f, 9.46f, 7.29f, +CUBIC_TO, 9.61f, 7.43f, 9.79f, 7.5f, 10, 7.5f, +CLOSE, +MOVE_TO, 10, 18, +CUBIC_TO, 8.9f, 18, 7.87f, 17.79f, 6.9f, 17.38f, +CUBIC_TO, 5.92f, 16.96f, 5.07f, 16.39f, 4.33f, 15.67f, +CUBIC_TO, 3.61f, 14.93f, 3.04f, 14.08f, 2.63f, 13.1f, +CUBIC_TO, 2.21f, 12.13f, 2, 11.1f, 2, 10, +CUBIC_TO, 2, 8.89f, 2.21f, 7.85f, 2.63f, 6.9f, +CUBIC_TO, 3.04f, 5.92f, 3.61f, 5.08f, 4.33f, 4.35f, +CUBIC_TO, 5.07f, 3.62f, 5.92f, 3.04f, 6.9f, 2.63f, +CUBIC_TO, 7.87f, 2.21f, 8.9f, 2, 10, 2, +CUBIC_TO, 11.11f, 2, 12.15f, 2.21f, 13.1f, 2.63f, +CUBIC_TO, 14.08f, 3.04f, 14.92f, 3.62f, 15.65f, 4.35f, +CUBIC_TO, 16.38f, 5.08f, 16.96f, 5.92f, 17.38f, 6.9f, +CUBIC_TO, 17.79f, 7.85f, 18, 8.89f, 18, 10, +CUBIC_TO, 18, 11.1f, 17.79f, 12.13f, 17.38f, 13.1f, +CUBIC_TO, 16.96f, 14.08f, 16.38f, 14.93f, 15.65f, 15.67f, +CUBIC_TO, 14.92f, 16.39f, 14.08f, 16.96f, 13.1f, 17.38f, +CUBIC_TO, 12.15f, 17.79f, 11.11f, 18, 10, 18, +CLOSE, +MOVE_TO, 10, 16.5f, +CUBIC_TO, 11.81f, 16.5f, 13.34f, 15.87f, 14.6f, 14.6f, +CUBIC_TO, 15.87f, 13.34f, 16.5f, 11.81f, 16.5f, 10, +CUBIC_TO, 16.5f, 8.19f, 15.87f, 6.66f, 14.6f, 5.4f, +CUBIC_TO, 13.34f, 4.13f, 11.81f, 3.5f, 10, 3.5f, +CUBIC_TO, 8.19f, 3.5f, 6.66f, 4.13f, 5.4f, 5.4f, +CUBIC_TO, 4.13f, 6.66f, 3.5f, 8.19f, 3.5f, 10, +CUBIC_TO, 3.5f, 11.81f, 4.13f, 13.34f, 5.4f, 14.6f, +CUBIC_TO, 6.66f, 15.87f, 8.19f, 16.5f, 10, 16.5f, +CLOSE, +NEW_PATH + + +CANVAS_DIMENSIONS, 16, +MOVE_TO, 8, 1, +CUBIC_TO, 4.13f, 1, 1, 4.13f, 1, 8, +CUBIC_TO, 1, 11.87f, 4.13f, 15, 8, 15, +CUBIC_TO, 11.87f, 15, 15, 11.87f, 15, 8, +CUBIC_TO, 15, 4.13f, 11.87f, 1, 8, 1, +CLOSE, +MOVE_TO, 8, 13.5f, +CUBIC_TO, 4.97f, 13.5f, 2.5f, 11.03f, 2.5f, 8, +CUBIC_TO, 2.5f, 4.97f, 4.97f, 2.5f, 8, 2.5f, +CUBIC_TO, 11.03f, 2.5f, 13.5f, 4.97f, 13.5f, 8, +CUBIC_TO, 13.5f, 11.03f, 11.03f, 13.5f, 8, 13.5f, +CLOSE, +NEW_PATH, +MOVE_TO, 8.75f, 7.5f, +H_LINE_TO, 7.25f, +V_LINE_TO, 11.5f, +H_LINE_TO, 8.75f, +V_LINE_TO, 7.5f, +CLOSE, +NEW_PATH, +MOVE_TO, 8, 6, +CUBIC_TO, 8.41f, 6, 8.75f, 5.66f, 8.75f, 5.25f, +CUBIC_TO, 8.75f, 4.84f, 8.41f, 4.5f, 8, 4.5f, +CUBIC_TO, 7.59f, 4.5f, 7.25f, 4.84f, 7.25f, 5.25f, +CUBIC_TO, 7.25f, 5.66f, 7.59f, 6, 8, 6, +CLOSE, +NEW_PATH
diff --git a/components/omnibox/browser/vector_icons/product_chrome_refresh.icon b/components/omnibox/browser/vector_icons/product_chrome_refresh.icon new file mode 100644 index 0000000..6f6ada3 --- /dev/null +++ b/components/omnibox/browser/vector_icons/product_chrome_refresh.icon
@@ -0,0 +1,101 @@ +// 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. + +CANVAS_DIMENSIONS, 32, +MOVE_TO, 10.67f, 16.1f, +CUBIC_TO, 10.67f, 17.57f, 11.19f, 18.82f, 12.23f, 19.87f, +CUBIC_TO, 13.28f, 20.91f, 14.53f, 21.43f, 16, 21.43f, +CUBIC_TO, 17.47f, 21.43f, 18.72f, 20.91f, 19.77f, 19.87f, +CUBIC_TO, 20.81f, 18.82f, 21.33f, 17.57f, 21.33f, 16.1f, +CUBIC_TO, 21.33f, 14.63f, 20.81f, 13.38f, 19.77f, 12.33f, +CUBIC_TO, 18.72f, 11.29f, 17.47f, 10.77f, 16, 10.77f, +CUBIC_TO, 14.53f, 10.77f, 13.28f, 11.29f, 12.23f, 12.33f, +CUBIC_TO, 11.19f, 13.38f, 10.67f, 14.63f, 10.67f, 16.1f, +CLOSE, +MOVE_TO, 16, 24.1f, +CUBIC_TO, 16.29f, 24.1f, 16.57f, 24.09f, 16.83f, 24.07f, +CUBIC_TO, 17.1f, 24.04f, 17.37f, 24, 17.63f, 23.93f, +LINE_TO, 14.5f, 29.33f, +CUBIC_TO, 11.14f, 28.96f, 8.33f, 27.52f, 6.07f, 25.03f, +CUBIC_TO, 3.8f, 22.52f, 2.67f, 19.54f, 2.67f, 16.1f, +CUBIC_TO, 2.67f, 15.17f, 2.76f, 14.27f, 2.93f, 13.4f, +CUBIC_TO, 3.11f, 12.51f, 3.38f, 11.67f, 3.73f, 10.87f, +LINE_TO, 9.07f, 20.1f, +CUBIC_TO, 9.76f, 21.3f, 10.71f, 22.27f, 11.93f, 23, +CUBIC_TO, 13.16f, 23.73f, 14.51f, 24.1f, 16, 24.1f, +CLOSE, +MOVE_TO, 16, 8.1f, +CUBIC_TO, 14.22f, 8.1f, 12.64f, 8.62f, 11.27f, 9.67f, +CUBIC_TO, 9.89f, 10.69f, 8.93f, 12, 8.4f, 13.6f, +LINE_TO, 5.27f, 8.2f, +CUBIC_TO, 6.49f, 6.56f, 8.02f, 5.24f, 9.87f, 4.27f, +CUBIC_TO, 11.73f, 3.27f, 13.78f, 2.77f, 16, 2.77f, +CUBIC_TO, 18.2f, 2.77f, 20.22f, 3.26f, 22.07f, 4.23f, +CUBIC_TO, 23.91f, 5.19f, 25.44f, 6.48f, 26.67f, 8.1f, +H_LINE_TO, 16, +CLOSE, +MOVE_TO, 28.2f, 10.77f, +CUBIC_TO, 28.58f, 11.59f, 28.86f, 12.44f, 29.03f, 13.33f, +CUBIC_TO, 29.23f, 14.22f, 29.33f, 15.14f, 29.33f, 16.1f, +CUBIC_TO, 29.33f, 19.54f, 28.2f, 22.51f, 25.93f, 25, +CUBIC_TO, 23.69f, 27.49f, 20.91f, 28.93f, 17.6f, 29.33f, +LINE_TO, 22.93f, 20.1f, +CUBIC_TO, 23.27f, 19.52f, 23.52f, 18.9f, 23.7f, 18.23f, +CUBIC_TO, 23.9f, 17.54f, 24, 16.83f, 24, 16.1f, +CUBIC_TO, 24, 15.06f, 23.81f, 14.09f, 23.43f, 13.2f, +CUBIC_TO, 23.08f, 12.29f, 22.58f, 11.48f, 21.93f, 10.77f, +H_LINE_TO, 28.2f, +CLOSE, +NEW_PATH + + + +CANVAS_DIMENSIONS, 16, +MOVE_TO, 5.25f, 8, +CUBIC_TO, 5.25f, 8.76f, 5.52f, 9.4f, 6.06f, 9.94f, +CUBIC_TO, 6.6f, 10.48f, 7.25f, 10.75f, 8, 10.75f, +CUBIC_TO, 8.75f, 10.75f, 9.4f, 10.48f, 9.94f, 9.94f, +CUBIC_TO, 10.48f, 9.4f, 10.75f, 8.75f, 10.75f, 8, +CUBIC_TO, 10.75f, 7.25f, 10.48f, 6.6f, 9.94f, 6.06f, +CUBIC_TO, 9.4f, 5.52f, 8.75f, 5.25f, 8, 5.25f, +CUBIC_TO, 7.25f, 5.25f, 6.6f, 5.52f, 6.06f, 6.06f, +CUBIC_TO, 5.52f, 6.6f, 5.25f, 7.25f, 5.25f, 8, +CLOSE, +MOVE_TO, 8, 12.25f, +CUBIC_TO, 8.15f, 12.25f, 8.3f, 12.25f, 8.44f, 12.23f, +CUBIC_TO, 8.58f, 12.22f, 8.72f, 12.19f, 8.86f, 12.16f, +LINE_TO, 7.22f, 15, +CUBIC_TO, 5.46f, 14.8f, 3.98f, 14.05f, 2.79f, 12.74f, +CUBIC_TO, 1.6f, 11.42f, 1, 9.86f, 1, 8.05f, +CUBIC_TO, 1, 7.56f, 1.05f, 7.09f, 1.14f, 6.63f, +CUBIC_TO, 1.23f, 6.16f, 1.37f, 5.72f, 1.56f, 5.3f, +LINE_TO, 4.36f, 10.15f, +CUBIC_TO, 4.72f, 10.78f, 5.22f, 11.29f, 5.86f, 11.67f, +CUBIC_TO, 6.5f, 12.05f, 7.21f, 12.25f, 8, 12.25f, +CLOSE, +MOVE_TO, 8, 3.85f, +CUBIC_TO, 7.07f, 3.85f, 6.24f, 4.12f, 5.52f, 4.67f, +CUBIC_TO, 4.8f, 5.21f, 4.29f, 5.9f, 4.01f, 6.74f, +LINE_TO, 2.36f, 3.9f, +CUBIC_TO, 3, 3.04f, 3.81f, 2.35f, 4.78f, 1.83f, +CUBIC_TO, 5.76f, 1.3f, 6.83f, 1.04f, 8, 1.04f, +CUBIC_TO, 9.17f, 1.04f, 10.22f, 1.3f, 11.19f, 1.81f, +CUBIC_TO, 12.16f, 2.31f, 12.96f, 2.99f, 13.61f, 3.84f, +H_LINE_TO, 8.01f, +LINE_TO, 8, 3.85f, +CLOSE, +MOVE_TO, 14.41f, 5.25f, +CUBIC_TO, 14.61f, 5.68f, 14.75f, 6.13f, 14.85f, 6.6f, +CUBIC_TO, 14.95f, 7.07f, 15.01f, 7.55f, 15.01f, 8.05f, +CUBIC_TO, 15.01f, 9.86f, 14.42f, 11.42f, 13.22f, 12.72f, +CUBIC_TO, 12.04f, 14.03f, 10.58f, 14.78f, 8.85f, 14.99f, +LINE_TO, 11.65f, 10.14f, +CUBIC_TO, 11.83f, 9.84f, 11.96f, 9.51f, 12.05f, 9.16f, +CUBIC_TO, 12.15f, 8.8f, 12.21f, 8.42f, 12.21f, 8.04f, +CUBIC_TO, 12.21f, 7.49f, 12.11f, 6.98f, 11.91f, 6.52f, +CUBIC_TO, 11.72f, 6.04f, 11.46f, 5.62f, 11.12f, 5.24f, +H_LINE_TO, 14.41f, +V_LINE_TO, 5.25f, +CLOSE, +NEW_PATH
diff --git a/components/omnibox/browser/vector_icons/star_active_chrome_refresh.icon b/components/omnibox/browser/vector_icons/star_active_chrome_refresh.icon new file mode 100644 index 0000000..6944b0be --- /dev/null +++ b/components/omnibox/browser/vector_icons/star_active_chrome_refresh.icon
@@ -0,0 +1,33 @@ +// 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. + +CANVAS_DIMENSIONS, 20, +MOVE_TO, 5.06f, 18, +LINE_TO, 6.38f, 12.46f, +LINE_TO, 2, 8.73f, +LINE_TO, 7.75f, 8.23f, +LINE_TO, 10, 3, +LINE_TO, 12.25f, 8.25f, +LINE_TO, 18, 8.73f, +LINE_TO, 13.63f, 12.46f, +LINE_TO, 14.94f, 18, +LINE_TO, 10, 15.06f, +LINE_TO, 5.06f, 18, +CLOSE, +NEW_PATH + +CANVAS_DIMENSIONS, 16, +MOVE_TO, 8, 11.45f, +LINE_TO, 12.33f, 14, +LINE_TO, 11.18f, 9.19f, +LINE_TO, 15, 5.95f, +LINE_TO, 9.97f, 5.54f, +LINE_TO, 8, 1, +LINE_TO, 6.03f, 5.54f, +LINE_TO, 1, 5.95f, +LINE_TO, 4.82f, 9.19f, +LINE_TO, 3.67f, 14, +LINE_TO, 8, 11.45f, +CLOSE, +NEW_PATH
diff --git a/components/omnibox/browser/vector_icons/star_chrome_refresh.icon b/components/omnibox/browser/vector_icons/star_chrome_refresh.icon new file mode 100644 index 0000000..de9e941 --- /dev/null +++ b/components/omnibox/browser/vector_icons/star_chrome_refresh.icon
@@ -0,0 +1,60 @@ +// 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. + +CANVAS_DIMENSIONS, 20, +MOVE_TO, 7.33f, 14.9f, +LINE_TO, 10, 13.31f, +LINE_TO, 12.69f, 14.9f, +LINE_TO, 11.98f, 11.9f, +LINE_TO, 14.29f, 9.92f, +LINE_TO, 11.23f, 9.65f, +LINE_TO, 10, 6.79f, +LINE_TO, 8.77f, 9.65f, +LINE_TO, 5.71f, 9.92f, +LINE_TO, 8.04f, 11.9f, +LINE_TO, 7.33f, 14.9f, +CLOSE, +MOVE_TO, 5.06f, 18, +LINE_TO, 6.38f, 12.46f, +LINE_TO, 2, 8.73f, +LINE_TO, 7.75f, 8.23f, +LINE_TO, 10, 3, +LINE_TO, 12.25f, 8.25f, +LINE_TO, 18, 8.73f, +LINE_TO, 13.63f, 12.46f, +LINE_TO, 14.94f, 18, +LINE_TO, 10, 15.06f, +LINE_TO, 5.06f, 18, +CLOSE, +NEW_PATH + + +CANVAS_DIMENSIONS, 16, +MOVE_TO, 15, 5.95f, +LINE_TO, 9.97f, 5.53f, +LINE_TO, 8, 1, +LINE_TO, 6.03f, 5.54f, +LINE_TO, 1, 5.96f, +LINE_TO, 4.82f, 9.2f, +LINE_TO, 3.67f, 14.01f, +LINE_TO, 8, 11.46f, +LINE_TO, 12.33f, 14.01f, +LINE_TO, 11.18f, 9.2f, +LINE_TO, 15, 5.96f, +V_LINE_TO, 5.95f, +CLOSE, +MOVE_TO, 10.05f, 10.91f, +LINE_TO, 8, 9.7f, +LINE_TO, 5.95f, 10.91f, +LINE_TO, 6.49f, 8.64f, +LINE_TO, 4.73f, 7.15f, +LINE_TO, 7.05f, 6.96f, +LINE_TO, 8, 4.77f, +LINE_TO, 8.95f, 6.96f, +LINE_TO, 11.27f, 7.15f, +LINE_TO, 9.51f, 8.64f, +LINE_TO, 10.05f, 10.91f, +V_LINE_TO, 10.91f, +CLOSE, +NEW_PATH
diff --git a/components/omnibox/common/omnibox_features.cc b/components/omnibox/common/omnibox_features.cc index 089c33e..f70d7c7 100644 --- a/components/omnibox/common/omnibox_features.cc +++ b/components/omnibox/common/omnibox_features.cc
@@ -481,6 +481,76 @@ "OmniboxSteadyStateTextStyle", base::FEATURE_DISABLED_BY_DEFAULT); +// If enabled, Omnibox "steady state" text color is updated to match GM3 +// guidelines. +BASE_FEATURE(kOmniboxSteadyStateTextColor, + "OmniboxSteadyStateTextColor", + base::FEATURE_DISABLED_BY_DEFAULT); + +// Specifies the GM3 omnibox text color in Dark Mode. +// +// In order to control the value of this param via Finch, the +// kOmniboxSteadyStateTextColor feature flag must be enabled. +// +// Enabling only the kChromeRefresh2023 flag, while leaving the +// kOmniboxSteadyStateTextColor flag disabled, will result in the param being +// locked to its default value and ignoring any overrides provided via Finch. +// +// If neither kChromeRefresh2023 nor kOmniboxSteadyStateTextColor are enabled, +// then this feature param will have zero effect on Chrome UI. +const base::FeatureParam<std::string> kOmniboxTextColorDarkMode( + &omnibox::kOmniboxSteadyStateTextColor, + "OmniboxTextColorDarkMode", + "0xE3E3E3"); + +// Specifies the GM3 omnibox text color in Dark Mode (dimmed). +// +// In order to control the value of this param via Finch, the +// kOmniboxSteadyStateTextColor feature flag must be enabled. +// +// Enabling only the kChromeRefresh2023 flag, while leaving the +// kOmniboxSteadyStateTextColor flag disabled, will result in the param being +// locked to its default value and ignoring any overrides provided via Finch. +// +// If neither kChromeRefresh2023 nor kOmniboxSteadyStateTextColor are enabled, +// then this feature param will have zero effect on Chrome UI. +const base::FeatureParam<std::string> kOmniboxTextColorDimmedDarkMode( + &omnibox::kOmniboxSteadyStateTextColor, + "OmniboxTextColorDimmedDarkMode", + "0xC7C7C7"); + +// Specifies the GM3 omnibox text color in Light Mode. +// +// In order to control the value of this param via Finch, the +// kOmniboxSteadyStateTextColor feature flag must be enabled. +// +// Enabling only the kChromeRefresh2023 flag, while leaving the +// kOmniboxSteadyStateTextColor flag disabled, will result in the param being +// locked to its default value and ignoring any overrides provided via Finch. +// +// If neither kChromeRefresh2023 nor kOmniboxSteadyStateTextColor are enabled, +// then this feature param will have zero effect on Chrome UI. +const base::FeatureParam<std::string> kOmniboxTextColorLightMode( + &omnibox::kOmniboxSteadyStateTextColor, + "OmniboxTextColorLightMode", + "0x1F1F1F"); + +// Specifies the GM3 omnibox text color in Light Mode (dimmed). +// +// In order to control the value of this param via Finch, the +// kOmniboxSteadyStateTextColor feature flag must be enabled. +// +// Enabling only the kChromeRefresh2023 flag, while leaving the +// kOmniboxSteadyStateTextColor flag disabled, will result in the param being +// locked to its default value and ignoring any overrides provided via Finch. +// +// If neither kChromeRefresh2023 nor kOmniboxSteadyStateTextColor are enabled, +// then this feature param will have zero effect on Chrome UI. +const base::FeatureParam<std::string> kOmniboxTextColorDimmedLightMode( + &omnibox::kOmniboxSteadyStateTextColor, + "OmniboxTextColorDimmedLightMode", + "0x474747"); + // If enabled, switching tabs will not restore the omnibox state. // TODO(manukh): Should also blur the omnibox on tab switch. BASE_FEATURE(kDiscardTemporaryInputOnTabSwitch,
diff --git a/components/omnibox/common/omnibox_features.h b/components/omnibox/common/omnibox_features.h index 0797b0c..f2fb1cd7 100644 --- a/components/omnibox/common/omnibox_features.h +++ b/components/omnibox/common/omnibox_features.h
@@ -117,6 +117,15 @@ BASE_DECLARE_FEATURE(kOmniboxSteadyStateHeight); BASE_DECLARE_FEATURE(kOmniboxSteadyStateTextStyle); + +BASE_DECLARE_FEATURE(kOmniboxSteadyStateTextColor); +// These feature params are located here, as opposed to omnibox_field_trial.h, +// in order to permit inclusion into (non-Omnibox) color mixer code. +extern const base::FeatureParam<std::string> kOmniboxTextColorDarkMode; +extern const base::FeatureParam<std::string> kOmniboxTextColorDimmedDarkMode; +extern const base::FeatureParam<std::string> kOmniboxTextColorLightMode; +extern const base::FeatureParam<std::string> kOmniboxTextColorDimmedLightMode; + BASE_DECLARE_FEATURE(kDiscardTemporaryInputOnTabSwitch); BASE_DECLARE_FEATURE(kRedoCurrentMatch); BASE_DECLARE_FEATURE(kRevertModelBeforeClosingPopup);
diff --git a/components/permissions/BUILD.gn b/components/permissions/BUILD.gn index 46a85b0b..36017e7 100644 --- a/components/permissions/BUILD.gn +++ b/components/permissions/BUILD.gn
@@ -25,8 +25,6 @@ "bluetooth_delegate_impl.h", "chooser_controller.cc", "chooser_controller.h", - "chooser_title_util.cc", - "chooser_title_util.h", "contexts/accessibility_permission_context.cc", "contexts/accessibility_permission_context.h", "contexts/bluetooth_chooser_context.cc", @@ -256,7 +254,6 @@ source_set("unit_tests") { testonly = true sources = [ - "chooser_title_util_unittest.cc", "contexts/camera_pan_tilt_zoom_permission_context_unittest.cc", "contexts/geolocation_permission_context_unittest.cc", "contexts/midi_permission_context_unittest.cc",
diff --git a/components/permissions/chooser_title_util.cc b/components/permissions/chooser_title_util.cc deleted file mode 100644 index baa5170..0000000 --- a/components/permissions/chooser_title_util.cc +++ /dev/null
@@ -1,25 +0,0 @@ -// Copyright 2021 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "components/permissions/chooser_title_util.h" - -#include "components/url_formatter/elide_url.h" -#include "content/public/browser/render_frame_host.h" -#include "ui/base/l10n/l10n_util.h" -#include "url/origin.h" - -namespace permissions { - -std::u16string CreateChooserTitle(content::RenderFrameHost* render_frame_host, - int title_string_id_origin) { - if (!render_frame_host) - return u""; - return l10n_util::GetStringFUTF16( - title_string_id_origin, - url_formatter::FormatOriginForSecurityDisplay( - render_frame_host->GetMainFrame()->GetLastCommittedOrigin(), - url_formatter::SchemeDisplay::OMIT_CRYPTOGRAPHIC)); -} - -} // namespace permissions
diff --git a/components/permissions/chooser_title_util.h b/components/permissions/chooser_title_util.h deleted file mode 100644 index 77d9a91b..0000000 --- a/components/permissions/chooser_title_util.h +++ /dev/null
@@ -1,24 +0,0 @@ -// Copyright 2021 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef COMPONENTS_PERMISSIONS_CHOOSER_TITLE_UTIL_H_ -#define COMPONENTS_PERMISSIONS_CHOOSER_TITLE_UTIL_H_ - -#include <string> - -namespace content { -class RenderFrameHost; -} - -namespace permissions { - -// Creates a title for a chooser using the origin of the main frame -// containing `render_frame_host`. Returns the empty string if -// `render_frame_host` is null. -std::u16string CreateChooserTitle(content::RenderFrameHost* render_frame_host, - int title_string_id_origin); - -} // namespace permissions - -#endif // COMPONENTS_PERMISSIONS_CHOOSER_TITLE_UTIL_H_
diff --git a/components/permissions/chooser_title_util_unittest.cc b/components/permissions/chooser_title_util_unittest.cc deleted file mode 100644 index 443a4cd..0000000 --- a/components/permissions/chooser_title_util_unittest.cc +++ /dev/null
@@ -1,41 +0,0 @@ -// Copyright 2022 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "components/permissions/chooser_title_util.h" - -#include "components/strings/grit/components_strings.h" -#include "content/public/test/navigation_simulator.h" -#include "content/public/test/test_renderer_host.h" -#include "url/gurl.h" -#include "url/origin.h" - -namespace permissions { -namespace { - -constexpr int kTitleResourceId = IDS_USB_DEVICE_CHOOSER_PROMPT_ORIGIN; - -using ChooserTitleTest = content::RenderViewHostTestHarness; - -TEST_F(ChooserTitleTest, NoFrame) { - EXPECT_EQ(u"", CreateChooserTitle(nullptr, kTitleResourceId)); -} - -TEST_F(ChooserTitleTest, FrameTree) { - NavigateAndCommit(GURL("https://main-frame.com")); - content::RenderFrameHost* subframe = - content::NavigationSimulator::NavigateAndCommitFromDocument( - GURL("https://sub-frame.com"), - content::RenderFrameHostTester::For(main_rfh()) - ->AppendChild("subframe")); - - EXPECT_EQ("main-frame.com", main_rfh()->GetLastCommittedOrigin().host()); - EXPECT_EQ(u"main-frame.com wants to connect", - CreateChooserTitle(main_rfh(), kTitleResourceId)); - EXPECT_EQ("sub-frame.com", subframe->GetLastCommittedOrigin().host()); - EXPECT_EQ(u"main-frame.com wants to connect", - CreateChooserTitle(subframe, kTitleResourceId)); -} - -} // namespace -} // namespace permissions
diff --git a/components/permissions/fake_bluetooth_chooser_controller.cc b/components/permissions/fake_bluetooth_chooser_controller.cc index 321de55f..dfa71dc 100644 --- a/components/permissions/fake_bluetooth_chooser_controller.cc +++ b/components/permissions/fake_bluetooth_chooser_controller.cc
@@ -15,7 +15,7 @@ FakeBluetoothChooserController::FakeBluetoothChooserController( std::vector<FakeDevice> devices) : ChooserController( - l10n_util::GetStringFUTF16(IDS_BLUETOOTH_DEVICE_CHOOSER_PROMPT_ORIGIN, + l10n_util::GetStringFUTF16(IDS_BLUETOOTH_DEVICE_CHOOSER_PROMPT, u"example.com")), devices_(std::move(devices)) {}
diff --git a/components/permissions/fake_usb_chooser_controller.cc b/components/permissions/fake_usb_chooser_controller.cc index 69de8b1..63e718c 100644 --- a/components/permissions/fake_usb_chooser_controller.cc +++ b/components/permissions/fake_usb_chooser_controller.cc
@@ -12,7 +12,7 @@ FakeUsbChooserController::FakeUsbChooserController(int device_count) : ChooserController(u""), device_count_(device_count) { set_title_for_testing(l10n_util::GetStringFUTF16( - IDS_USB_DEVICE_CHOOSER_PROMPT_ORIGIN, u"example.com")); + IDS_USB_DEVICE_CHOOSER_PROMPT, u"example.com")); } std::u16string FakeUsbChooserController::GetNoOptionsText() const {
diff --git a/components/permissions_strings.grdp b/components/permissions_strings.grdp index dd35fc8d..05b347e 100644 --- a/components/permissions_strings.grdp +++ b/components/permissions_strings.grdp
@@ -161,7 +161,7 @@ </message> <!-- Device Chooser --> <if expr="not is_android"> - <message name="IDS_BLUETOOTH_DEVICE_CHOOSER_PROMPT_ORIGIN" desc="The label that is used to introduce Bluetooth chooser details to the user in a popup when it is from a website."> + <message name="IDS_BLUETOOTH_DEVICE_CHOOSER_PROMPT" desc="The label that is used to introduce Bluetooth chooser details to the user in a popup."> <ph name="Origin">$1<ex>www.google.com</ex></ph> wants to pair </message> <message name="IDS_BLUETOOTH_DEVICE_CHOOSER_NO_DEVICES_FOUND_PROMPT" desc="The label shown to the user to inform them that no Bluetooth devices were found matching the requirements that the application provided."> @@ -183,7 +183,7 @@ <!-- Bluetooth Scanning Prompt --> <if expr="not is_android"> - <message name="IDS_BLUETOOTH_SCANNING_PROMPT_ORIGIN" desc="The label that is used to introduce Bluetooth scanning prompt details to the user when it is from a website."> + <message name="IDS_BLUETOOTH_SCANNING_PROMPT" desc="The label that is used to introduce Bluetooth scanning prompt details to the user."> <ph name="Origin">$1<ex>www.google.com</ex></ph> wants to scan for nearby Bluetooth devices. The following devices have been found: </message> <message name="IDS_BLUETOOTH_SCANNING_DEVICE_UNKNOWN" desc="Text to identify Bluetooth devices of unknown or unsupported class."> @@ -206,7 +206,7 @@ <message name="IDS_DEVICE_CHOOSER_NO_DEVICES_FOUND_PROMPT" desc="The label shown to the user to inform them that no USB devices were found matching the requirements that the application provided."> No compatible devices found. </message> - <message name="IDS_USB_DEVICE_CHOOSER_PROMPT_ORIGIN" desc="The label that is used to introduce USB chooser details to the user in a popup when it is from a website."> + <message name="IDS_USB_DEVICE_CHOOSER_PROMPT" desc="The label that is used to introduce USB chooser details to the user in a popup."> <ph name="Origin">$1<ex>www.google.com</ex></ph> wants to connect </message> <message name="IDS_USB_DEVICE_CHOOSER_CONNECT_BUTTON_TEXT" desc="Label on the button that closes the USB chooser popup and connects the selected device.">
diff --git a/components/permissions_strings_grdp/IDS_BLUETOOTH_DEVICE_CHOOSER_PROMPT_ORIGIN.png.sha1 b/components/permissions_strings_grdp/IDS_BLUETOOTH_DEVICE_CHOOSER_PROMPT.png.sha1 similarity index 100% rename from components/permissions_strings_grdp/IDS_BLUETOOTH_DEVICE_CHOOSER_PROMPT_ORIGIN.png.sha1 rename to components/permissions_strings_grdp/IDS_BLUETOOTH_DEVICE_CHOOSER_PROMPT.png.sha1
diff --git a/components/permissions_strings_grdp/IDS_BLUETOOTH_SCANNING_PROMPT.png.sha1 b/components/permissions_strings_grdp/IDS_BLUETOOTH_SCANNING_PROMPT.png.sha1 new file mode 100644 index 0000000..97c8251d --- /dev/null +++ b/components/permissions_strings_grdp/IDS_BLUETOOTH_SCANNING_PROMPT.png.sha1
@@ -0,0 +1 @@ +02c71330856e0abf140c701afa2bd3be2fe08b80 \ No newline at end of file
diff --git a/components/permissions_strings_grdp/IDS_USB_DEVICE_CHOOSER_PROMPT_ORIGIN.png.sha1 b/components/permissions_strings_grdp/IDS_USB_DEVICE_CHOOSER_PROMPT.png.sha1 similarity index 100% rename from components/permissions_strings_grdp/IDS_USB_DEVICE_CHOOSER_PROMPT_ORIGIN.png.sha1 rename to components/permissions_strings_grdp/IDS_USB_DEVICE_CHOOSER_PROMPT.png.sha1
diff --git a/components/saved_tab_groups/saved_tab_group_model_unittest.cc b/components/saved_tab_groups/saved_tab_group_model_unittest.cc index 3b9fce33..6b3e229 100644 --- a/components/saved_tab_groups/saved_tab_group_model_unittest.cc +++ b/components/saved_tab_groups/saved_tab_group_model_unittest.cc
@@ -26,20 +26,6 @@ namespace { -base::GUID GenerateNextGUID() { - static uint64_t guid_increment; - if (!guid_increment) { - guid_increment = 0; - } - - uint64_t kBytes[] = {0, guid_increment}; - base::GUID guid = - base::GUID::ParseCaseInsensitive(base::RandomDataToGUIDString(kBytes)); - - guid_increment++; - return guid; -} - void CompareSavedTabGroupTabs(const std::vector<SavedTabGroupTab>& v1, const std::vector<SavedTabGroupTab>& v2) { ASSERT_EQ(v1.size(), v2.size()); @@ -88,7 +74,7 @@ } SavedTabGroup CreateTestSavedTabGroup() { - base::GUID id = GenerateNextGUID(); + base::GUID id = base::GUID::GenerateRandomV4(); const std::u16string title = u"Test Test"; const tab_groups::TabGroupColorId& color = tab_groups::TabGroupColorId::kBlue; @@ -183,9 +169,9 @@ class SavedTabGroupModelTest : public ::testing::Test { protected: SavedTabGroupModelTest() - : id_1_(GenerateNextGUID()), - id_2_(GenerateNextGUID()), - id_3_(GenerateNextGUID()) {} + : id_1_(base::GUID::GenerateRandomV4()), + id_2_(base::GUID::GenerateRandomV4()), + id_3_(base::GUID::GenerateRandomV4()) {} ~SavedTabGroupModelTest() override { RemoveTestData(); } @@ -263,7 +249,8 @@ EXPECT_TRUE(saved_tab_group_model_->Contains(id_1_)); EXPECT_TRUE(saved_tab_group_model_->Contains(id_2_)); EXPECT_TRUE(saved_tab_group_model_->Contains(id_3_)); - EXPECT_FALSE(saved_tab_group_model_->Contains(GenerateNextGUID())); + EXPECT_FALSE( + saved_tab_group_model_->Contains(base::GUID::GenerateRandomV4())); } // Tests that the SavedTabGroupModel::GetIndexOf preserves the order the @@ -300,7 +287,7 @@ // Tests that SavedTabGroupModel::Add adds an extra element into the model and // keeps the data. TEST_F(SavedTabGroupModelTest, AddNewElement) { - base::GUID id_4 = GenerateNextGUID(); + base::GUID id_4 = base::GUID::GenerateRandomV4(); const std::u16string title_4 = u"Test Test"; const tab_groups::TabGroupColorId& color_4 = tab_groups::TabGroupColorId::kBlue; @@ -976,13 +963,13 @@ TEST_F(SavedTabGroupModelObserverTest, MoveElement) { SavedTabGroup stg_1(std::u16string(u"stg_1"), tab_groups::TabGroupColorId::kGrey, {}, - GenerateNextGUID()); + base::GUID::GenerateRandomV4()); SavedTabGroup stg_2(std::u16string(u"stg_2"), tab_groups::TabGroupColorId::kGrey, {}, - GenerateNextGUID()); + base::GUID::GenerateRandomV4()); SavedTabGroup stg_3(std::u16string(u"stg_3"), tab_groups::TabGroupColorId::kGrey, {}, - GenerateNextGUID()); + base::GUID::GenerateRandomV4()); saved_tab_group_model_->Add(stg_1); saved_tab_group_model_->Add(stg_2); @@ -1002,7 +989,7 @@ SavedTabGroup matching_group = CreateTestSavedTabGroup(); base::GUID matching_group_guid = matching_group.saved_guid(); - base::GUID matching_tab_guid = GenerateNextGUID(); + base::GUID matching_tab_guid = base::GUID::GenerateRandomV4(); base::Token matching_local_tab_id = base::Token::CreateRandom(); SavedTabGroupTab tab(GURL(url::kAboutBlankURL), std::u16string(u"title"), @@ -1023,8 +1010,8 @@ saved_tab_group_model_->GetGroupContainingTab(matching_local_tab_id)); // Expect GetGroupContainingTab to return null when there is no match. - EXPECT_EQ(nullptr, - saved_tab_group_model_->GetGroupContainingTab(GenerateNextGUID())); + EXPECT_EQ(nullptr, saved_tab_group_model_->GetGroupContainingTab( + base::GUID::GenerateRandomV4())); EXPECT_EQ(nullptr, saved_tab_group_model_->GetGroupContainingTab(base::Token())); }
diff --git a/components/supervised_user/core/common/features.h b/components/supervised_user/core/common/features.h index 2469c3d2..1c86faf5 100644 --- a/components/supervised_user/core/common/features.h +++ b/components/supervised_user/core/common/features.h
@@ -23,6 +23,8 @@ BASE_DECLARE_FEATURE(kFilterWebsitesForSupervisedUsersOnDesktopAndIOS); BASE_DECLARE_FEATURE(kEnableExtensionsPermissionsForSupervisedUsersOnDesktop); +BASE_DECLARE_FEATURE(kLocalExtensionApprovalsV2); + // Returns whether refreshed version of the website filter interstitial is // enabled. bool IsWebFilterInterstitialRefreshEnabled();
diff --git a/components/variations/service/BUILD.gn b/components/variations/service/BUILD.gn index 6d822ac..a5ff8a82 100644 --- a/components/variations/service/BUILD.gn +++ b/components/variations/service/BUILD.gn
@@ -4,6 +4,7 @@ import("//build/buildflag_header.gni") import("//build/config/chrome_build.gni") +import("//build/config/chromeos/ui_mode.gni") declare_args() { # Set to true make a build that disables activation of field trial tests @@ -70,6 +71,13 @@ "//services/network/public/cpp", "//ui/base", ] + + if (is_chromeos_ash) { + deps += [ + "//chromeos/ash/components/dbus/featured", + "//components/variations/cros:proto", + ] + } } source_set("unit_tests") { @@ -105,4 +113,11 @@ "//services/network/public/cpp", "//testing/gtest", ] + + if (is_chromeos_ash) { + deps += [ + "//chromeos/ash/components/dbus/featured", + "//components/variations/cros:proto", + ] + } }
diff --git a/components/variations/service/DEPS b/components/variations/service/DEPS index 0c3020c..45260d32 100644 --- a/components/variations/service/DEPS +++ b/components/variations/service/DEPS
@@ -1,4 +1,5 @@ include_rules = [ + "+chromeos/ash/components/dbus/featured", "+components/encrypted_messages", "+components/language/core/browser", "+components/metrics",
diff --git a/components/variations/service/safe_seed_manager.cc b/components/variations/service/safe_seed_manager.cc index 3b0fcf7f9..07f54571 100644 --- a/components/variations/service/safe_seed_manager.cc +++ b/components/variations/service/safe_seed_manager.cc
@@ -9,6 +9,7 @@ #include "base/cxx17_backports.h" #include "base/metrics/histogram_functions.h" #include "base/metrics/histogram_macros.h" +#include "build/chromeos_buildflags.h" #include "components/prefs/pref_registry.h" #include "components/prefs/pref_registry_simple.h" #include "components/prefs/pref_service.h" @@ -17,6 +18,12 @@ #include "components/variations/variations_seed_store.h" #include "components/variations/variations_switches.h" +#if BUILDFLAG(IS_CHROMEOS_ASH) +#include "base/functional/callback.h" +#include "chromeos/ash/components/dbus/featured/featured_client.h" +#include "components/variations/cros/featured.pb.h" +#endif // BUILDFLAG(IS_CHROMEOS_ASH) + namespace variations { // Consecutive seed fetch failures are, unfortunately, a bit more common. As of @@ -49,6 +56,12 @@ constexpr int kFetchFailureStreakSafeSeedThreshold = 25; constexpr int kFetchFailureStreakNullSeedThreshold = 50; +#if BUILDFLAG(IS_CHROMEOS_ASH) +// Number of attempts to send the safe seed from Chrome to CrOS platforms before +// giving up. +constexpr int kSendPlatformSafeSeedMaxAttempts = 2; +#endif // BUILDFLAG(IS_CHROMEOS_ASH) + SafeSeedManager::SafeSeedManager(PrefService* local_state) : local_state_(local_state) { int num_failed_fetches = @@ -129,6 +142,13 @@ active_seed_state_->seed_milestone, *active_seed_state_->client_filterable_state, active_seed_state_->seed_fetch_time); +#if BUILDFLAG(IS_CHROMEOS_ASH) + // `SendSafeSeedToPlatform` will send the safe seed at most twice. + // This is a best effort attempt and it is possible that the safe seed for + // platform and Chrome are different if sending the safe seed fails twice. + send_seed_to_platform_attempts_ = 0; + SendSafeSeedToPlatform(GetSafeSeedStateForPlatform()); +#endif // BUILDFLAG(IS_CHROMEOS_ASH) // The active seed state is only needed for the first time this code path is // reached, so free up its memory once the data is no longer needed. @@ -157,4 +177,48 @@ SafeSeedManager::ActiveSeedState::~ActiveSeedState() = default; +#if BUILDFLAG(IS_CHROMEOS_ASH) +featured::SeedDetails SafeSeedManager::GetSafeSeedStateForPlatform() { + featured::SeedDetails safe_seed; + safe_seed.set_compressed_data(active_seed_state_->seed_data); + safe_seed.set_locale(active_seed_state_->client_filterable_state->locale); + safe_seed.set_milestone(active_seed_state_->seed_milestone); + safe_seed.set_permanent_consistency_country( + active_seed_state_->client_filterable_state + ->permanent_consistency_country); + safe_seed.set_session_consistency_country( + active_seed_state_->client_filterable_state->session_consistency_country); + safe_seed.set_signature(active_seed_state_->base64_seed_signature); + safe_seed.set_date(active_seed_state_->client_filterable_state->reference_date + .ToDeltaSinceWindowsEpoch() + .InMilliseconds()); + safe_seed.set_fetch_time( + active_seed_state_->seed_fetch_time.ToDeltaSinceWindowsEpoch() + .InMilliseconds()); + + return safe_seed; +} + +void SafeSeedManager::MaybeRetrySendSafeSeed( + const featured::SeedDetails& safe_seed, + bool success) { + // Do not retry after two failed attempts. + if (!success && + send_seed_to_platform_attempts_ < kSendPlatformSafeSeedMaxAttempts) { + SendSafeSeedToPlatform(safe_seed); + } +} + +void SafeSeedManager::SendSafeSeedToPlatform( + const featured::SeedDetails& safe_seed) { + send_seed_to_platform_attempts_++; + ash::featured::FeaturedClient* client = ash::featured::FeaturedClient::Get(); + if (client) { + client->HandleSeedFetched( + safe_seed, base::BindOnce(&SafeSeedManager::MaybeRetrySendSafeSeed, + weak_ptr_factory_.GetWeakPtr(), safe_seed)); + } +} +#endif // BUILDFLAG(IS_CHROMEOS_ASH) + } // namespace variations
diff --git a/components/variations/service/safe_seed_manager.h b/components/variations/service/safe_seed_manager.h index f2d0a5b..0e626ae 100644 --- a/components/variations/service/safe_seed_manager.h +++ b/components/variations/service/safe_seed_manager.h
@@ -8,8 +8,15 @@ #include <memory> #include <string> +#include "base/gtest_prod_util.h" #include "base/memory/raw_ptr.h" +#include "base/memory/weak_ptr.h" #include "base/time/time.h" +#include "build/chromeos_buildflags.h" + +#if BUILDFLAG(IS_CHROMEOS_ASH) +#include "components/variations/cros/featured.pb.h" +#endif // BUILDFLAG(IS_CHROMEOS_ASH) class PrefRegistrySimple; class PrefService; @@ -86,6 +93,8 @@ void RecordSuccessfulFetch(VariationsSeedStore* seed_store); private: + FRIEND_TEST_ALL_PREFIXES(SafeSeedManagerTest, GetSafeSeedStateForPlatform); + // The combined server and client state needed to save an active seed as a // safe seed. Not set when running in safe mode. struct ActiveSeedState { @@ -121,6 +130,28 @@ // The pref service used to persist the variations seed. Weak reference; must // outlive |this| instance. raw_ptr<PrefService> local_state_; + +#if BUILDFLAG(IS_CHROMEOS_ASH) + // Gets the combined server and client state used for early boot variations + // platform disaster recovery. + featured::SeedDetails GetSafeSeedStateForPlatform(); + + // Retries sending the safe seed to platform. Does not retry after two failed + // attempts. + void MaybeRetrySendSafeSeed(const featured::SeedDetails& safe_seed, + bool success); + + // Sends the safe seed to the platform. + void SendSafeSeedToPlatform(const featured::SeedDetails& safe_seed); + + // A counter that keeps track of how many times the current safe seed is sent + // to platform. + size_t send_seed_to_platform_attempts_ = 0; + + // Note: This should remain the last member so it'll be destroyed and + // invalidate its weak pointers before any other members are destroyed. + base::WeakPtrFactory<SafeSeedManager> weak_ptr_factory_{this}; +#endif // BUILDFLAG(IS_CHROMEOS_ASH) }; } // namespace variations
diff --git a/components/variations/service/safe_seed_manager_unittest.cc b/components/variations/service/safe_seed_manager_unittest.cc index 3ab567e..a95404bb 100644 --- a/components/variations/service/safe_seed_manager_unittest.cc +++ b/components/variations/service/safe_seed_manager_unittest.cc
@@ -11,6 +11,7 @@ #include "base/command_line.h" #include "base/test/metrics/histogram_tester.h" #include "base/time/time.h" +#include "build/chromeos_buildflags.h" #include "components/metrics/clean_exit_beacon.h" #include "components/prefs/testing_pref_service.h" #include "components/variations/client_filterable_state.h" @@ -20,7 +21,13 @@ #include "components/variations/variations_test_utils.h" #include "testing/gtest/include/gtest/gtest.h" +#if BUILDFLAG(IS_CHROMEOS_ASH) +#include "chromeos/ash/components/dbus/featured/fake_featured_client.h" +#include "components/variations/cros/featured.pb.h" +#endif // BUILDFLAG(IS_CHROMEOS_ASH) + namespace variations { + namespace { const char kTestSeed[] = "compressed, base-64 encoded serialized seed data"; @@ -172,6 +179,83 @@ EXPECT_EQ(base::Time(), seed_store.fetch_time()); } +#if BUILDFLAG(IS_CHROMEOS_ASH) +TEST_F(SafeSeedManagerTest, GetSafeSeedStateForPlatform) { + SafeSeedManager safe_seed_manager(&prefs_); + FakeSeedStore seed_store(&prefs_); + SetDefaultActiveState(&safe_seed_manager, &prefs_); + + featured::SeedDetails platform_seed = + safe_seed_manager.GetSafeSeedStateForPlatform(); + + EXPECT_EQ(kTestSeed, platform_seed.compressed_data()); + EXPECT_EQ(kTestSignature, platform_seed.signature()); + EXPECT_EQ(kTestSeedMilestone, platform_seed.milestone()); + EXPECT_EQ(kTestLocale, platform_seed.locale()); + EXPECT_EQ(kTestPermanentConsistencyCountry, + platform_seed.permanent_consistency_country()); + EXPECT_EQ(kTestSessionConsistencyCountry, + platform_seed.session_consistency_country()); + EXPECT_EQ(base::Time::UnixEpoch().ToDeltaSinceWindowsEpoch().InMilliseconds(), + platform_seed.date()); + EXPECT_EQ(GetTestFetchTime().ToDeltaSinceWindowsEpoch().InMilliseconds(), + platform_seed.fetch_time()); +} + +TEST_F(SafeSeedManagerTest, SendSafeSeedToPlatform_SucceedFirstAttempt) { + SafeSeedManager safe_seed_manager(&prefs_); + FakeSeedStore seed_store(&prefs_); + SetDefaultActiveState(&safe_seed_manager, &prefs_); + + ash::featured::FeaturedClient::InitializeFake(); + ash::featured::FakeFeaturedClient* client = + ash::featured::FakeFeaturedClient::Get(); + client->AddResponse(true); + + safe_seed_manager.RecordSuccessfulFetch(&seed_store); + ExpectDefaultActiveState(seed_store); + EXPECT_EQ(client->handle_seed_fetched_attempts(), 1); + + ash::featured::FeaturedClient::Shutdown(); +} + +TEST_F(SafeSeedManagerTest, SendSafeSeedToPlatform_FailFirstAttempt) { + SafeSeedManager safe_seed_manager(&prefs_); + FakeSeedStore seed_store(&prefs_); + SetDefaultActiveState(&safe_seed_manager, &prefs_); + + ash::featured::FeaturedClient::InitializeFake(); + ash::featured::FakeFeaturedClient* client = + ash::featured::FakeFeaturedClient::Get(); + client->AddResponse(false); + client->AddResponse(true); + + safe_seed_manager.RecordSuccessfulFetch(&seed_store); + ExpectDefaultActiveState(seed_store); + EXPECT_EQ(client->handle_seed_fetched_attempts(), 2); + + ash::featured::FeaturedClient::Shutdown(); +} + +TEST_F(SafeSeedManagerTest, SendSafeSeedToPlatform_FailTwoAttempts) { + SafeSeedManager safe_seed_manager(&prefs_); + FakeSeedStore seed_store(&prefs_); + SetDefaultActiveState(&safe_seed_manager, &prefs_); + + ash::featured::FeaturedClient::InitializeFake(); + ash::featured::FakeFeaturedClient* client = + ash::featured::FakeFeaturedClient::Get(); + client->AddResponse(false); + client->AddResponse(false); + + safe_seed_manager.RecordSuccessfulFetch(&seed_store); + ExpectDefaultActiveState(seed_store); + EXPECT_EQ(client->handle_seed_fetched_attempts(), 2); + + ash::featured::FeaturedClient::Shutdown(); +} +#endif // BUILDFLAG(IS_CHROMEOS_ASH) + TEST_F(SafeSeedManagerTest, FetchFailureMetrics_DefaultPrefs) { base::HistogramTester histogram_tester; SafeSeedManager safe_seed_manager(&prefs_);
diff --git a/components/vector_icons/BUILD.gn b/components/vector_icons/BUILD.gn index 959e1f8b..f61e17c 100644 --- a/components/vector_icons/BUILD.gn +++ b/components/vector_icons/BUILD.gn
@@ -22,6 +22,7 @@ "bluetooth_connected.icon", "bluetooth_scanning.icon", "business.icon", + "business_chrome_refresh.icon", "call.icon", "call_end.icon", "cancel.icon", @@ -92,6 +93,7 @@ "midi.icon", "midi_off.icon", "not_secure_warning.icon", + "not_secure_warning_chrome_refresh.icon", "notification_warning.icon", "notifications.icon", "notifications_off.icon", @@ -110,6 +112,7 @@ "save_original_file.icon", "screen_share.icon", "search.icon", + "search_chrome_refresh.icon", "select_window.icon", "sensors.icon", "serial_port.icon",
diff --git a/components/vector_icons/business_chrome_refresh.icon b/components/vector_icons/business_chrome_refresh.icon new file mode 100644 index 0000000..2588f08 --- /dev/null +++ b/components/vector_icons/business_chrome_refresh.icon
@@ -0,0 +1,268 @@ +// 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. + + +CANVAS_DIMENSIONS, 24, +MOVE_TO, 2, 21, +V_LINE_TO, 3, +H_LINE_TO, 12, +V_LINE_TO, 7, +H_LINE_TO, 22, +V_LINE_TO, 21, +H_LINE_TO, 2, +CLOSE, +MOVE_TO, 4, 19, +H_LINE_TO, 6, +V_LINE_TO, 17, +H_LINE_TO, 4, +V_LINE_TO, 19, +CLOSE, +MOVE_TO, 4, 15, +H_LINE_TO, 6, +V_LINE_TO, 13, +H_LINE_TO, 4, +V_LINE_TO, 15, +CLOSE, +MOVE_TO, 4, 11, +H_LINE_TO, 6, +V_LINE_TO, 9, +H_LINE_TO, 4, +V_LINE_TO, 11, +CLOSE, +MOVE_TO, 4, 7, +H_LINE_TO, 6, +V_LINE_TO, 5, +H_LINE_TO, 4, +V_LINE_TO, 7, +CLOSE, +MOVE_TO, 8, 19, +H_LINE_TO, 10, +V_LINE_TO, 17, +H_LINE_TO, 8, +V_LINE_TO, 19, +CLOSE, +MOVE_TO, 8, 15, +H_LINE_TO, 10, +V_LINE_TO, 13, +H_LINE_TO, 8, +V_LINE_TO, 15, +CLOSE, +MOVE_TO, 8, 11, +H_LINE_TO, 10, +V_LINE_TO, 9, +H_LINE_TO, 8, +V_LINE_TO, 11, +CLOSE, +MOVE_TO, 8, 7, +H_LINE_TO, 10, +V_LINE_TO, 5, +H_LINE_TO, 8, +V_LINE_TO, 7, +CLOSE, +MOVE_TO, 12, 19, +H_LINE_TO, 20, +V_LINE_TO, 9, +H_LINE_TO, 12, +V_LINE_TO, 11, +H_LINE_TO, 14, +V_LINE_TO, 13, +H_LINE_TO, 12, +V_LINE_TO, 15, +H_LINE_TO, 14, +V_LINE_TO, 17, +H_LINE_TO, 12, +V_LINE_TO, 19, +CLOSE, +MOVE_TO, 16, 13, +V_LINE_TO, 11, +H_LINE_TO, 18, +V_LINE_TO, 13, +H_LINE_TO, 16, +CLOSE, +MOVE_TO, 16, 17, +V_LINE_TO, 15, +H_LINE_TO, 18, +V_LINE_TO, 17, +H_LINE_TO, 16, +CLOSE + + + + +CANVAS_DIMENSIONS, 20, +MOVE_TO, 2, 17, +V_LINE_TO, 3, +H_LINE_TO, 10, +V_LINE_TO, 6, +H_LINE_TO, 18, +V_LINE_TO, 17, +H_LINE_TO, 2, +CLOSE, +MOVE_TO, 3.5f, 15.5f, +H_LINE_TO, 5, +V_LINE_TO, 14, +H_LINE_TO, 3.5f, +V_LINE_TO, 15.5f, +CLOSE, +MOVE_TO, 3.5f, 12.33f, +H_LINE_TO, 5, +V_LINE_TO, 10.83f, +H_LINE_TO, 3.5f, +V_LINE_TO, 12.33f, +CLOSE, +MOVE_TO, 3.5f, 9.17f, +H_LINE_TO, 5, +V_LINE_TO, 7.67f, +H_LINE_TO, 3.5f, +V_LINE_TO, 9.17f, +CLOSE, +MOVE_TO, 3.5f, 6, +H_LINE_TO, 5, +V_LINE_TO, 4.5f, +H_LINE_TO, 3.5f, +V_LINE_TO, 6, +CLOSE, +MOVE_TO, 7, 15.5f, +H_LINE_TO, 8.5f, +V_LINE_TO, 14, +H_LINE_TO, 7, +V_LINE_TO, 15.5f, +CLOSE, +MOVE_TO, 7, 12.33f, +H_LINE_TO, 8.5f, +V_LINE_TO, 10.83f, +H_LINE_TO, 7, +V_LINE_TO, 12.33f, +CLOSE, +MOVE_TO, 7, 9.17f, +H_LINE_TO, 8.5f, +V_LINE_TO, 7.67f, +H_LINE_TO, 7, +V_LINE_TO, 9.17f, +CLOSE, +MOVE_TO, 7, 6, +H_LINE_TO, 8.5f, +V_LINE_TO, 4.5f, +H_LINE_TO, 7, +V_LINE_TO, 6, +CLOSE, +MOVE_TO, 10, 15.5f, +H_LINE_TO, 16.5f, +V_LINE_TO, 7.5f, +H_LINE_TO, 10, +V_LINE_TO, 9.17f, +H_LINE_TO, 11.5f, +V_LINE_TO, 10.67f, +H_LINE_TO, 10, +V_LINE_TO, 12.33f, +H_LINE_TO, 11.5f, +V_LINE_TO, 13.83f, +H_LINE_TO, 10, +V_LINE_TO, 15.5f, +CLOSE, +MOVE_TO, 13.5f, 10.67f, +V_LINE_TO, 9.17f, +H_LINE_TO, 15, +V_LINE_TO, 10.67f, +H_LINE_TO, 13.5f, +CLOSE, +MOVE_TO, 13.5f, 13.83f, +V_LINE_TO, 12.33f, +H_LINE_TO, 15, +V_LINE_TO, 13.83f, +H_LINE_TO, 13.5f, +CLOSE, +NEW_PATH + + + + + +CANVAS_DIMENSIONS, 16, +MOVE_TO, 12, 7, +H_LINE_TO, 11, +V_LINE_TO, 8, +H_LINE_TO, 12, +V_LINE_TO, 7, +CLOSE, +NEW_PATH, +MOVE_TO, 12, 10, +H_LINE_TO, 11, +V_LINE_TO, 11, +H_LINE_TO, 12, +V_LINE_TO, 10, +CLOSE, +NEW_PATH, +MOVE_TO, 8, 4, +V_LINE_TO, 2, +H_LINE_TO, 1, +V_LINE_TO, 14, +H_LINE_TO, 15, +V_LINE_TO, 4, +H_LINE_TO, 8, +CLOSE, +MOVE_TO, 4, 12, +H_LINE_TO, 3, +V_LINE_TO, 11, +H_LINE_TO, 4, +V_LINE_TO, 12, +CLOSE, +MOVE_TO, 4, 9.67f, +H_LINE_TO, 3, +V_LINE_TO, 8.67f, +H_LINE_TO, 4, +V_LINE_TO, 9.67f, +CLOSE, +MOVE_TO, 4, 7.34f, +H_LINE_TO, 3, +V_LINE_TO, 6.34f, +H_LINE_TO, 4, +V_LINE_TO, 7.34f, +CLOSE, +MOVE_TO, 4, 5.01f, +H_LINE_TO, 3, +V_LINE_TO, 4.01f, +H_LINE_TO, 4, +V_LINE_TO, 5.01f, +CLOSE, +MOVE_TO, 6, 12.01f, +H_LINE_TO, 5, +V_LINE_TO, 11.01f, +H_LINE_TO, 6, +V_LINE_TO, 12.01f, +CLOSE, +MOVE_TO, 6, 9.68f, +H_LINE_TO, 5, +V_LINE_TO, 8.68f, +H_LINE_TO, 6, +V_LINE_TO, 9.68f, +CLOSE, +MOVE_TO, 6, 7.35f, +H_LINE_TO, 5, +V_LINE_TO, 6.35f, +H_LINE_TO, 6, +V_LINE_TO, 7.35f, +CLOSE, +MOVE_TO, 6, 5.02f, +H_LINE_TO, 5, +V_LINE_TO, 4.02f, +H_LINE_TO, 6, +V_LINE_TO, 5.02f, +CLOSE, +MOVE_TO, 13.5f, 12.52f, +H_LINE_TO, 8, +V_LINE_TO, 11.02f, +H_LINE_TO, 9, +V_LINE_TO, 10.02f, +H_LINE_TO, 8, +V_LINE_TO, 8.02f, +H_LINE_TO, 9, +V_LINE_TO, 7.02f, +H_LINE_TO, 8, +V_LINE_TO, 5.52f, +H_LINE_TO, 13.5f, +V_LINE_TO, 12.52f, +CLOSE, +NEW_PATH +
diff --git a/components/vector_icons/not_secure_warning_chrome_refresh.icon b/components/vector_icons/not_secure_warning_chrome_refresh.icon new file mode 100644 index 0000000..875406a --- /dev/null +++ b/components/vector_icons/not_secure_warning_chrome_refresh.icon
@@ -0,0 +1,59 @@ +// 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. + +CANVAS_DIMENSIONS, 32, +MOVE_TO, 1.33f, 28, +LINE_TO, 16, 2.67f, +LINE_TO, 30.67f, 28, +H_LINE_TO, 1.33f, +CLOSE, +MOVE_TO, 5.57f, 25.57f, +H_LINE_TO, 26.43f, +LINE_TO, 16, 7.57f, +LINE_TO, 5.57f, 25.57f, +CLOSE, +MOVE_TO, 16.03f, 24.03f, +CUBIC_TO, 16.39f, 24.03f, 16.68f, 23.92f, 16.9f, 23.7f, +CUBIC_TO, 17.14f, 23.46f, 17.27f, 23.16f, 17.27f, 22.8f, +CUBIC_TO, 17.27f, 22.47f, 17.14f, 22.19f, 16.9f, 21.97f, +CUBIC_TO, 16.68f, 21.72f, 16.39f, 21.6f, 16.03f, 21.6f, +CUBIC_TO, 15.7f, 21.6f, 15.41f, 21.72f, 15.17f, 21.97f, +CUBIC_TO, 14.94f, 22.19f, 14.83f, 22.47f, 14.83f, 22.8f, +CUBIC_TO, 14.83f, 23.16f, 14.94f, 23.46f, 15.17f, 23.7f, +CUBIC_TO, 15.41f, 23.92f, 15.7f, 24.03f, 16.03f, 24.03f, +CLOSE, +MOVE_TO, 14.83f, 20.13f, +H_LINE_TO, 17.27f, +V_LINE_TO, 13.2f, +H_LINE_TO, 14.83f, +V_LINE_TO, 20.13f, +CLOSE, +NEW_PATH + +CANVAS_DIMENSIONS, 16, +MOVE_TO, 8, 2, +LINE_TO, 1, 14, +H_LINE_TO, 15, +LINE_TO, 8, 2, +CLOSE, +MOVE_TO, 8, 4.98f, +LINE_TO, 12.39f, 12.5f, +H_LINE_TO, 3.61f, +LINE_TO, 8, 4.98f, +CLOSE, +NEW_PATH, +MOVE_TO, 8.75f, 7, +H_LINE_TO, 7.25f, +V_LINE_TO, 10, +H_LINE_TO, 8.75f, +V_LINE_TO, 7, +CLOSE, +NEW_PATH, +MOVE_TO, 8, 12, +CUBIC_TO, 8.41f, 12, 8.75f, 11.66f, 8.75f, 11.25f, +CUBIC_TO, 8.75f, 10.84f, 8.41f, 10.5f, 8, 10.5f, +CUBIC_TO, 7.59f, 10.5f, 7.25f, 10.84f, 7.25f, 11.25f, +CUBIC_TO, 7.25f, 11.66f, 7.59f, 12, 8, 12, +CLOSE, +NEW_PATH
diff --git a/components/vector_icons/search_chrome_refresh.icon b/components/vector_icons/search_chrome_refresh.icon new file mode 100644 index 0000000..508b567 --- /dev/null +++ b/components/vector_icons/search_chrome_refresh.icon
@@ -0,0 +1,59 @@ +// 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. + +CANVAS_DIMENSIONS, 32, +MOVE_TO, 26.27f, 28, +LINE_TO, 17.73f, 19.47f, +CUBIC_TO, 17.07f, 20.02f, 16.3f, 20.46f, 15.43f, 20.77f, +CUBIC_TO, 14.57f, 21.08f, 13.63f, 21.23f, 12.63f, 21.23f, +CUBIC_TO, 10.23f, 21.23f, 8.19f, 20.4f, 6.5f, 18.73f, +CUBIC_TO, 4.83f, 17.04f, 4, 15, 4, 12.6f, +CUBIC_TO, 4, 10.2f, 4.83f, 8.17f, 6.5f, 6.5f, +CUBIC_TO, 8.19f, 4.83f, 10.23f, 4, 12.63f, 4, +CUBIC_TO, 15.03f, 4, 17.07f, 4.83f, 18.73f, 6.5f, +CUBIC_TO, 20.4f, 8.17f, 21.23f, 10.2f, 21.23f, 12.6f, +CUBIC_TO, 21.23f, 13.58f, 21.08f, 14.51f, 20.77f, 15.4f, +CUBIC_TO, 20.46f, 16.27f, 20.02f, 17.04f, 19.47f, 17.73f, +LINE_TO, 28, 26.27f, +LINE_TO, 26.27f, 28, +CLOSE, +MOVE_TO, 12.63f, 18.8f, +CUBIC_TO, 14.34f, 18.8f, 15.8f, 18.2f, 17, 17, +CUBIC_TO, 18.2f, 15.78f, 18.8f, 14.31f, 18.8f, 12.6f, +CUBIC_TO, 18.8f, 10.89f, 18.2f, 9.43f, 17, 8.23f, +CUBIC_TO, 15.8f, 7.03f, 14.34f, 6.43f, 12.63f, 6.43f, +CUBIC_TO, 10.92f, 6.43f, 9.46f, 7.03f, 8.23f, 8.23f, +CUBIC_TO, 7.03f, 9.43f, 6.43f, 10.89f, 6.43f, 12.6f, +CUBIC_TO, 6.43f, 14.31f, 7.03f, 15.78f, 8.23f, 17, +CUBIC_TO, 9.46f, 18.2f, 10.92f, 18.8f, 12.63f, 18.8f, +CLOSE, +NEW_PATH + +CANVAS_DIMENSIONS, 20, +MOVE_TO, 15.94f, 17, +LINE_TO, 10.96f, 12.02f, +CUBIC_TO, 10.54f, 12.33f, 10.08f, 12.57f, 9.58f, 12.75f, +CUBIC_TO, 9.08f, 12.92f, 8.56f, 13, 8, 13, +CUBIC_TO, 6.61f, 13, 5.43f, 12.51f, 4.46f, 11.54f, +CUBIC_TO, 3.49f, 10.57f, 3, 9.39f, 3, 8, +CUBIC_TO, 3, 6.61f, 3.49f, 5.43f, 4.46f, 4.46f, +CUBIC_TO, 5.43f, 3.49f, 6.61f, 3, 8, 3, +CUBIC_TO, 9.39f, 3, 10.57f, 3.49f, 11.54f, 4.46f, +CUBIC_TO, 12.51f, 5.43f, 13, 6.61f, 13, 8, +CUBIC_TO, 13, 8.56f, 12.91f, 9.08f, 12.73f, 9.58f, +CUBIC_TO, 12.56f, 10.08f, 12.33f, 10.54f, 12.02f, 10.96f, +LINE_TO, 17, 15.94f, +LINE_TO, 15.94f, 17, +CLOSE, +MOVE_TO, 8, 11.5f, +CUBIC_TO, 8.97f, 11.5f, 9.8f, 11.16f, 10.48f, 10.48f, +CUBIC_TO, 11.16f, 9.8f, 11.5f, 8.97f, 11.5f, 8, +CUBIC_TO, 11.5f, 7.03f, 11.16f, 6.2f, 10.48f, 5.52f, +CUBIC_TO, 9.8f, 4.84f, 8.97f, 4.5f, 8, 4.5f, +CUBIC_TO, 7.03f, 4.5f, 6.2f, 4.84f, 5.52f, 5.52f, +CUBIC_TO, 4.84f, 6.2f, 4.5f, 7.03f, 4.5f, 8, +CUBIC_TO, 4.5f, 8.97f, 4.84f, 9.8f, 5.52f, 10.48f, +CUBIC_TO, 6.2f, 11.16f, 7.03f, 11.5f, 8, 11.5f, +CLOSE, +NEW_PATH
diff --git a/components/webdata/common/web_database_backend.cc b/components/webdata/common/web_database_backend.cc index 2ebcbab..4b9e566 100644 --- a/components/webdata/common/web_database_backend.cc +++ b/components/webdata/common/web_database_backend.cc
@@ -118,7 +118,7 @@ diagnostics_ = db_->GetDiagnosticInfo(error, statement); diagnostics_ += sql::GetCorruptFileDiagnosticsInfo(db_path_); - db_->GetSQLConnection()->RazeAndClose(); + db_->GetSQLConnection()->RazeAndPoison(); } }
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn index f6a8eda..b5644ad 100644 --- a/content/browser/BUILD.gn +++ b/content/browser/BUILD.gn
@@ -2463,10 +2463,10 @@ ] deps += [ "//third_party/abseil-cpp:absl", - "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.accessibility.semantics:fuchsia.accessibility.semantics_hlcpp", + "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.accessibility.semantics:fuchsia.accessibility.semantics_cpp", + "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.accessibility.semantics:fuchsia.accessibility.semantics_cpp_hlcpp_conversion", "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.mediacodec:fuchsia.mediacodec_hlcpp", "//third_party/fuchsia-sdk/sdk/pkg/inspect", - "//third_party/fuchsia-sdk/sdk/pkg/scenic_cpp", "//third_party/fuchsia-sdk/sdk/pkg/sys_inspect_cpp", "//third_party/fuchsia-sdk/sdk/pkg/zx", "//ui/accessibility",
diff --git a/content/browser/accessibility/accessibility_tree_formatter_fuchsia.cc b/content/browser/accessibility/accessibility_tree_formatter_fuchsia.cc index eeb47a6..383b020 100644 --- a/content/browser/accessibility/accessibility_tree_formatter_fuchsia.cc +++ b/content/browser/accessibility/accessibility_tree_formatter_fuchsia.cc
@@ -12,10 +12,10 @@ namespace content { namespace { -using FuchsiaAction = fuchsia::accessibility::semantics::Action; -using FuchsiaCheckedState = fuchsia::accessibility::semantics::CheckedState; -using FuchsiaRole = fuchsia::accessibility::semantics::Role; -using FuchsiaToggledState = fuchsia::accessibility::semantics::ToggledState; +using FuchsiaAction = fuchsia_accessibility_semantics::Action; +using FuchsiaCheckedState = fuchsia_accessibility_semantics::CheckedState; +using FuchsiaRole = fuchsia_accessibility_semantics::Role; +using FuchsiaToggledState = fuchsia_accessibility_semantics::ToggledState; constexpr const char* const kBoolAttributes[] = { "hidden", "focusable", "has_input_focus", "is_keyboard_key", "selected", @@ -40,53 +40,53 @@ std::string FuchsiaRoleToString(const FuchsiaRole role) { switch (role) { - case FuchsiaRole::BUTTON: + case FuchsiaRole::kButton: return "BUTTON"; - case FuchsiaRole::CELL: + case FuchsiaRole::kCell: return "CELL"; - case FuchsiaRole::CHECK_BOX: + case FuchsiaRole::kCheckBox: return "CHECK_BOX"; - case FuchsiaRole::COLUMN_HEADER: + case FuchsiaRole::kColumnHeader: return "COLUMN_HEADER"; - case FuchsiaRole::GRID: + case FuchsiaRole::kGrid: return "GRID"; - case FuchsiaRole::HEADER: + case FuchsiaRole::kHeader: return "HEADER"; - case FuchsiaRole::IMAGE: + case FuchsiaRole::kImage: return "IMAGE"; - case FuchsiaRole::LINK: + case FuchsiaRole::kLink: return "LINK"; - case FuchsiaRole::LIST: + case FuchsiaRole::kList: return "LIST"; - case FuchsiaRole::LIST_ELEMENT: + case FuchsiaRole::kListElement: return "LIST_ELEMENT"; - case FuchsiaRole::LIST_ELEMENT_MARKER: + case FuchsiaRole::kListElementMarker: return "LIST_ELEMENT_MARKER"; - case FuchsiaRole::PARAGRAPH: + case FuchsiaRole::kParagraph: return "PARAGRAPH"; - case FuchsiaRole::RADIO_BUTTON: + case FuchsiaRole::kRadioButton: return "RADIO_BUTTON"; - case FuchsiaRole::ROW_GROUP: + case FuchsiaRole::kRowGroup: return "ROW_GROUP"; - case FuchsiaRole::ROW_HEADER: + case FuchsiaRole::kRowHeader: return "ROW_HEADER"; - case FuchsiaRole::SEARCH_BOX: + case FuchsiaRole::kSearchBox: return "SEARCH_BOX"; - case FuchsiaRole::SLIDER: + case FuchsiaRole::kSlider: return "SLIDER"; - case FuchsiaRole::STATIC_TEXT: + case FuchsiaRole::kStaticText: return "STATIC_TEXT"; - case FuchsiaRole::TABLE: + case FuchsiaRole::kTable: return "TABLE"; - case FuchsiaRole::TABLE_ROW: + case FuchsiaRole::kTableRow: return "TABLE_ROW"; - case FuchsiaRole::TEXT_FIELD: + case FuchsiaRole::kTextField: return "TEXT_FIELD"; - case FuchsiaRole::TEXT_FIELD_WITH_COMBO_BOX: + case FuchsiaRole::kTextFieldWithComboBox: return "TEXT_FIELD_WITH_COMBO_BOX"; - case FuchsiaRole::TOGGLE_SWITCH: + case FuchsiaRole::kToggleSwitch: return "TOGGLE_SWITCH"; - case FuchsiaRole::UNKNOWN: + case FuchsiaRole::kUnknown: return "UNKNOWN"; default: NOTREACHED(); @@ -96,19 +96,19 @@ std::string FuchsiaActionToString(FuchsiaAction action) { switch (action) { - case FuchsiaAction::DEFAULT: + case FuchsiaAction::kDefault: return "DEFAULT"; - case FuchsiaAction::DECREMENT: + case FuchsiaAction::kDecrement: return "DECREMENT"; - case FuchsiaAction::INCREMENT: + case FuchsiaAction::kIncrement: return "INCREMENT"; - case FuchsiaAction::SECONDARY: + case FuchsiaAction::kSecondary: return "SECONDARY"; - case FuchsiaAction::SET_FOCUS: + case FuchsiaAction::kSetFocus: return "SET_FOCUS"; - case FuchsiaAction::SET_VALUE: + case FuchsiaAction::kSetValue: return "SET_VALUE"; - case FuchsiaAction::SHOW_ON_SCREEN: + case FuchsiaAction::kShowOnScreen: return "SHOW_ON_SCREEN"; default: NOTREACHED(); @@ -130,13 +130,13 @@ std::string CheckedStateToString(const FuchsiaCheckedState checked_state) { switch (checked_state) { - case FuchsiaCheckedState::NONE: + case FuchsiaCheckedState::kNone: return "NONE"; - case FuchsiaCheckedState::CHECKED: + case FuchsiaCheckedState::kChecked: return "CHECKED"; - case FuchsiaCheckedState::UNCHECKED: + case FuchsiaCheckedState::kUnchecked: return "UNCHECKED"; - case FuchsiaCheckedState::MIXED: + case FuchsiaCheckedState::kMixed: return "MIXED"; default: NOTREACHED(); @@ -146,11 +146,11 @@ std::string ToggledStateToString(const FuchsiaToggledState toggled_state) { switch (toggled_state) { - case FuchsiaToggledState::ON: + case FuchsiaToggledState::kOn: return "ON"; - case FuchsiaToggledState::OFF: + case FuchsiaToggledState::kOff: return "OFF"; - case FuchsiaToggledState::INDETERMINATE: + case FuchsiaToggledState::kIndeterminate: return "INDETERMINATE"; default: NOTREACHED(); @@ -159,29 +159,30 @@ } std::string ViewportOffsetToString( - const fuchsia::ui::gfx::vec2& viewport_offset) { - return base::StringPrintf("(%.1f, %.1f)", viewport_offset.x, - viewport_offset.y); + const fuchsia_ui_gfx::Vec2& viewport_offset) { + return base::StringPrintf("(%.1f, %.1f)", viewport_offset.x(), + viewport_offset.y()); } -std::string Vec3ToString(const fuchsia::ui::gfx::vec3& vec) { - return base::StringPrintf("(%.1f, %.1f, %.1f)", vec.x, vec.y, vec.z); +std::string Vec3ToString(const fuchsia_ui_gfx::Vec3& vec) { + return base::StringPrintf("(%.1f, %.1f, %.1f)", vec.x(), vec.y(), vec.z()); } -std::string Mat4ToString(const fuchsia::ui::gfx::mat4& mat) { +std::string Mat4ToString(const fuchsia_ui_gfx::Mat4& mat) { std::string retval = "{ "; for (int i = 0; i < 4; i++) { - retval.append(base::StringPrintf( - "col%d: (%.1f,%.1f,%.1f,%.1f), ", i, mat.matrix[i * 4], - mat.matrix[i * 4 + 1], mat.matrix[i * 4 + 2], mat.matrix[i * 4 + 3])); + retval.append( + base::StringPrintf("col%d: (%.1f,%.1f,%.1f,%.1f), ", i, + mat.matrix()[i * 4], mat.matrix()[i * 4 + 1], + mat.matrix()[i * 4 + 2], mat.matrix()[i * 4 + 3])); } return retval.append(" }"); } -std::string LocationToString(const fuchsia::ui::gfx::BoundingBox& location) { +std::string LocationToString(const fuchsia_ui_gfx::BoundingBox& location) { return base::StringPrintf("{ min: %s, max: %s }", - Vec3ToString(location.min).c_str(), - Vec3ToString(location.max).c_str()); + Vec3ToString(location.min()).c_str(), + Vec3ToString(location.max()).c_str()); } } // namespace @@ -223,10 +224,10 @@ base::Value::List children; - fuchsia::accessibility::semantics::Node fuchsia_node = + fuchsia_accessibility_semantics::Node fuchsia_node = static_cast<const BrowserAccessibilityFuchsia&>(node).ToFuchsiaNodeData(); - for (uint32_t child_id : fuchsia_node.child_ids()) { + for (uint32_t child_id : fuchsia_node.child_ids().value()) { ui::AXPlatformNodeFuchsia* child_node = static_cast<ui::AXPlatformNodeFuchsia*>( ui::AXPlatformNodeBase::GetFromUniqueId(child_id)); @@ -259,135 +260,159 @@ CHECK(browser_accessibility_fuchsia); - const fuchsia::accessibility::semantics::Node& fuchsia_node = + const fuchsia_accessibility_semantics::Node& fuchsia_node = browser_accessibility_fuchsia->ToFuchsiaNodeData(); // Add fuchsia node attributes. - dict->Set("role", FuchsiaRoleToString(fuchsia_node.role())); + dict->Set("role", FuchsiaRoleToString(fuchsia_node.role().value())); - dict->Set("actions", FuchsiaActionsToString(fuchsia_node.actions())); + dict->Set("actions", FuchsiaActionsToString(fuchsia_node.actions().value())); - if (fuchsia_node.has_attributes()) { - const fuchsia::accessibility::semantics::Attributes& attributes = - fuchsia_node.attributes(); + if (fuchsia_node.attributes()) { + const fuchsia_accessibility_semantics::Attributes& attributes = + fuchsia_node.attributes().value(); - if (attributes.has_label() && !attributes.label().empty()) - dict->Set("label", attributes.label()); - - if (attributes.has_secondary_label() && - !attributes.secondary_label().empty()) { - dict->Set("secondary_label", attributes.secondary_label()); + if (attributes.label() && !attributes.label()->empty()) { + dict->Set("label", attributes.label().value()); } - if (attributes.has_range()) { - const auto& range_attributes = attributes.range(); - - if (range_attributes.has_min_value()) - dict->Set("min_value", range_attributes.min_value()); - - if (range_attributes.has_max_value()) - dict->Set("max_value", range_attributes.max_value()); - - if (range_attributes.has_step_delta()) - dict->Set("step_delta", range_attributes.step_delta()); + if (attributes.secondary_label() && + !attributes.secondary_label()->empty()) { + dict->Set("secondary_label", attributes.secondary_label().value()); } - if (attributes.has_table_attributes()) { - const auto& table_attributes = attributes.table_attributes(); + if (attributes.range()) { + const auto& range_attributes = attributes.range().value(); - if (table_attributes.has_number_of_rows()) + if (range_attributes.min_value()) { + dict->Set("min_value", range_attributes.min_value().value()); + } + + if (range_attributes.max_value()) { + dict->Set("max_value", range_attributes.max_value().value()); + } + + if (range_attributes.step_delta()) { + dict->Set("step_delta", range_attributes.step_delta().value()); + } + } + + if (attributes.table_attributes()) { + const auto& table_attributes = attributes.table_attributes().value(); + + if (table_attributes.number_of_rows()) { dict->Set("number_of_rows", - static_cast<int>(table_attributes.number_of_rows())); + static_cast<int>(table_attributes.number_of_rows().value())); + } - if (table_attributes.has_number_of_columns()) { - dict->Set("number_of_columns", - static_cast<int>(table_attributes.number_of_columns())); + if (table_attributes.number_of_columns()) { + dict->Set( + "number_of_columns", + static_cast<int>(table_attributes.number_of_columns().value())); } } - if (attributes.has_table_row_attributes()) { - const auto& table_row_attributes = attributes.table_row_attributes(); + if (attributes.table_row_attributes()) { + const auto& table_row_attributes = + attributes.table_row_attributes().value(); - if (table_row_attributes.has_row_index()) + if (table_row_attributes.row_index()) { dict->Set("row_index", - static_cast<int>(table_row_attributes.row_index())); + static_cast<int>(table_row_attributes.row_index().value())); + } } - if (attributes.has_table_cell_attributes()) { - const auto& table_cell_attributes = attributes.table_cell_attributes(); + if (attributes.table_cell_attributes()) { + const auto& table_cell_attributes = + attributes.table_cell_attributes().value(); - if (table_cell_attributes.has_row_index()) + if (table_cell_attributes.row_index()) { dict->Set("cell_row_index", - static_cast<int>(table_cell_attributes.row_index())); - - if (table_cell_attributes.has_column_index()) { - dict->Set("cell_column_index", - static_cast<int>(table_cell_attributes.column_index())); + static_cast<int>(table_cell_attributes.row_index().value())); } - if (table_cell_attributes.has_row_span()) + if (table_cell_attributes.column_index()) { + dict->Set( + "cell_column_index", + static_cast<int>(table_cell_attributes.column_index().value())); + } + + if (table_cell_attributes.row_span()) { dict->Set("cell_row_span", - static_cast<int>(table_cell_attributes.row_span())); + static_cast<int>(table_cell_attributes.row_span().value())); + } - if (table_cell_attributes.has_column_span()) { - dict->Set("cell_column_span", - static_cast<int>(table_cell_attributes.column_span())); + if (table_cell_attributes.column_span()) { + dict->Set( + "cell_column_span", + static_cast<int>(table_cell_attributes.column_span().value())); } } - if (attributes.has_list_attributes()) { + if (attributes.list_attributes() && attributes.list_attributes()->size()) { dict->Set("list_size", - static_cast<int>(attributes.list_attributes().size())); + static_cast<int>(attributes.list_attributes()->size().value())); } - if (attributes.has_list_element_attributes()) { + if (attributes.list_element_attributes() && + attributes.list_element_attributes()->index()) { dict->Set("list_element_index", - static_cast<int>(attributes.list_element_attributes().index())); + static_cast<int>( + attributes.list_element_attributes()->index().value())); } - if (attributes.has_is_keyboard_key()) - dict->Set("is_keyboard_key", attributes.is_keyboard_key()); + if (attributes.is_keyboard_key()) { + dict->Set("is_keyboard_key", attributes.is_keyboard_key().value()); + } } - if (fuchsia_node.has_states()) { - const fuchsia::accessibility::semantics::States& states = - fuchsia_node.states(); + if (fuchsia_node.states()) { + const fuchsia_accessibility_semantics::States& states = + fuchsia_node.states().value(); - if (states.has_selected()) - dict->Set("selected", states.selected()); - - if (states.has_checked_state()) { - dict->Set("checked_state", CheckedStateToString(states.checked_state())); + if (states.selected()) { + dict->Set("selected", states.selected().value()); } - if (states.has_hidden()) - dict->Set("hidden", states.hidden()); + if (states.checked_state()) { + dict->Set("checked_state", + CheckedStateToString(states.checked_state().value())); + } - if (states.has_value() && !states.value().empty()) - dict->Set("value", states.value()); + if (states.hidden()) { + dict->Set("hidden", states.hidden().value()); + } - if (states.has_viewport_offset()) { + if (states.value() && !states.value()->empty()) { + dict->Set("value", states.value().value()); + } + + if (states.viewport_offset()) { dict->Set("viewport_offset", - ViewportOffsetToString(states.viewport_offset())); + ViewportOffsetToString(states.viewport_offset().value())); } - if (states.has_toggled_state()) { - dict->Set("toggled_state", ToggledStateToString(states.toggled_state())); + if (states.toggled_state()) { + dict->Set("toggled_state", + ToggledStateToString(states.toggled_state().value())); } - if (states.has_focusable()) - dict->Set("focusable", states.focusable()); + if (states.focusable()) { + dict->Set("focusable", states.focusable().value()); + } - if (states.has_has_input_focus()) - dict->Set("has_input_focus", states.has_input_focus()); + if (states.has_input_focus()) { + dict->Set("has_input_focus", states.has_input_focus().value()); + } } - if (fuchsia_node.has_location()) - dict->Set("location", LocationToString(fuchsia_node.location())); + if (fuchsia_node.location()) { + dict->Set("location", LocationToString(fuchsia_node.location().value())); + } - if (fuchsia_node.has_transform()) { + if (fuchsia_node.transform()) { dict->Set("transform", - Mat4ToString(fuchsia_node.node_to_container_transform())); + Mat4ToString(fuchsia_node.node_to_container_transform().value())); } }
diff --git a/content/browser/accessibility/browser_accessibility_fuchsia.cc b/content/browser/accessibility/browser_accessibility_fuchsia.cc index a0f6ba15..c2f033c1 100644 --- a/content/browser/accessibility/browser_accessibility_fuchsia.cc +++ b/content/browser/accessibility/browser_accessibility_fuchsia.cc
@@ -4,7 +4,7 @@ #include "content/browser/accessibility/browser_accessibility_fuchsia.h" -#include <lib/ui/scenic/cpp/commands.h> +#include <fidl/fuchsia.accessibility.semantics/cpp/hlcpp_conversion.h> #include "base/fuchsia/fuchsia_logging.h" #include "content/browser/accessibility/browser_accessibility_manager_fuchsia.h" @@ -15,7 +15,7 @@ namespace content { using AXRole = ax::mojom::Role; -using FuchsiaRole = fuchsia::accessibility::semantics::Role; +using FuchsiaRole = fuchsia_accessibility_semantics::Role; BrowserAccessibilityFuchsia::BrowserAccessibilityFuchsia( BrowserAccessibilityManager* manager, @@ -50,21 +50,19 @@ return static_cast<uint32_t>(GetUniqueId()); } -fuchsia::accessibility::semantics::Node +fuchsia_accessibility_semantics::Node BrowserAccessibilityFuchsia::ToFuchsiaNodeData() const { - fuchsia::accessibility::semantics::Node fuchsia_node_data; - - fuchsia_node_data.set_node_id(GetFuchsiaNodeID()); - fuchsia_node_data.set_role(GetFuchsiaRole()); - fuchsia_node_data.set_states(GetFuchsiaStates()); - fuchsia_node_data.set_attributes(GetFuchsiaAttributes()); - fuchsia_node_data.set_actions(GetFuchsiaActions()); - fuchsia_node_data.set_location(GetFuchsiaLocation()); - fuchsia_node_data.set_node_to_container_transform(GetFuchsiaTransform()); - fuchsia_node_data.set_container_id(GetOffsetContainerOrRootNodeID()); - fuchsia_node_data.set_child_ids(GetFuchsiaChildIDs()); - - return fuchsia_node_data; + return {{ + .node_id = GetFuchsiaNodeID(), + .role = GetFuchsiaRole(), + .states = GetFuchsiaStates(), + .attributes = GetFuchsiaAttributes(), + .actions = GetFuchsiaActions(), + .child_ids = GetFuchsiaChildIDs(), + .location = GetFuchsiaLocation(), + .container_id = GetOffsetContainerOrRootNodeID(), + .node_to_container_transform = GetFuchsiaTransform(), + }}; } void BrowserAccessibilityFuchsia::OnDataChanged() { @@ -107,104 +105,103 @@ return child_ids; } -std::vector<fuchsia::accessibility::semantics::Action> +std::vector<fuchsia_accessibility_semantics::Action> BrowserAccessibilityFuchsia::GetFuchsiaActions() const { - std::vector<fuchsia::accessibility::semantics::Action> actions; + std::vector<fuchsia_accessibility_semantics::Action> actions; if (HasAction(ax::mojom::Action::kDoDefault) || GetData().GetDefaultActionVerb() != ax::mojom::DefaultActionVerb::kNone) { - actions.push_back(fuchsia::accessibility::semantics::Action::DEFAULT); + actions.push_back(fuchsia_accessibility_semantics::Action::kDefault); } if (HasAction(ax::mojom::Action::kFocus)) - actions.push_back(fuchsia::accessibility::semantics::Action::SET_FOCUS); + actions.push_back(fuchsia_accessibility_semantics::Action::kSetFocus); if (HasAction(ax::mojom::Action::kSetValue)) - actions.push_back(fuchsia::accessibility::semantics::Action::SET_VALUE); + actions.push_back(fuchsia_accessibility_semantics::Action::kSetValue); if (HasAction(ax::mojom::Action::kScrollToMakeVisible)) { - actions.push_back( - fuchsia::accessibility::semantics::Action::SHOW_ON_SCREEN); + actions.push_back(fuchsia_accessibility_semantics::Action::kShowOnScreen); } return actions; } -fuchsia::accessibility::semantics::Role +fuchsia_accessibility_semantics::Role BrowserAccessibilityFuchsia::GetFuchsiaRole() const { auto role = GetRole(); switch (role) { case AXRole::kButton: - return FuchsiaRole::BUTTON; + return FuchsiaRole::kButton; case AXRole::kCell: - return FuchsiaRole::CELL; + return FuchsiaRole::kCell; case AXRole::kCheckBox: - return FuchsiaRole::CHECK_BOX; + return FuchsiaRole::kCheckBox; case AXRole::kColumnHeader: - return FuchsiaRole::COLUMN_HEADER; + return FuchsiaRole::kColumnHeader; case AXRole::kGrid: - return FuchsiaRole::GRID; + return FuchsiaRole::kGrid; case AXRole::kHeader: - return FuchsiaRole::HEADER; + return FuchsiaRole::kHeader; case AXRole::kImage: - return FuchsiaRole::IMAGE; + return FuchsiaRole::kImage; case AXRole::kLink: - return FuchsiaRole::LINK; + return FuchsiaRole::kLink; case AXRole::kList: - return FuchsiaRole::LIST; + return FuchsiaRole::kList; case AXRole::kListItem: - return FuchsiaRole::LIST_ELEMENT; + return FuchsiaRole::kListElement; case AXRole::kListMarker: - return FuchsiaRole::LIST_ELEMENT_MARKER; + return FuchsiaRole::kListElementMarker; case AXRole::kParagraph: - return FuchsiaRole::PARAGRAPH; + return FuchsiaRole::kParagraph; case AXRole::kRadioButton: - return FuchsiaRole::RADIO_BUTTON; + return FuchsiaRole::kRadioButton; case AXRole::kRowGroup: - return FuchsiaRole::ROW_GROUP; + return FuchsiaRole::kRowGroup; case AXRole::kSearchBox: - return FuchsiaRole::SEARCH_BOX; + return FuchsiaRole::kSearchBox; case AXRole::kSlider: - return FuchsiaRole::SLIDER; + return FuchsiaRole::kSlider; case AXRole::kStaticText: - return FuchsiaRole::STATIC_TEXT; + return FuchsiaRole::kStaticText; case AXRole::kTable: - return FuchsiaRole::TABLE; + return FuchsiaRole::kTable; case AXRole::kRow: - return FuchsiaRole::TABLE_ROW; + return FuchsiaRole::kTableRow; case AXRole::kTextField: - return FuchsiaRole::TEXT_FIELD; + return FuchsiaRole::kTextField; case AXRole::kTextFieldWithComboBox: - return FuchsiaRole::TEXT_FIELD_WITH_COMBO_BOX; + return FuchsiaRole::kTextFieldWithComboBox; default: - return FuchsiaRole::UNKNOWN; + return FuchsiaRole::kUnknown; } } -fuchsia::accessibility::semantics::States +fuchsia_accessibility_semantics::States BrowserAccessibilityFuchsia::GetFuchsiaStates() const { - fuchsia::accessibility::semantics::States states; + fuchsia_accessibility_semantics::States states; // Convert checked state. if (HasIntAttribute(ax::mojom::IntAttribute::kCheckedState)) { ax::mojom::CheckedState ax_state = GetData().GetCheckedState(); switch (ax_state) { case ax::mojom::CheckedState::kNone: - states.set_checked_state( - fuchsia::accessibility::semantics::CheckedState::NONE); + states.checked_state( + fuchsia_accessibility_semantics::CheckedState::kNone); break; case ax::mojom::CheckedState::kTrue: - states.set_checked_state( - fuchsia::accessibility::semantics::CheckedState::CHECKED); + states.checked_state( + fuchsia_accessibility_semantics::CheckedState::kChecked); break; case ax::mojom::CheckedState::kFalse: - states.set_checked_state( - fuchsia::accessibility::semantics::CheckedState::UNCHECKED); + states.checked_state( + fuchsia_accessibility_semantics::CheckedState::kUnchecked); break; case ax::mojom::CheckedState::kMixed: - states.set_checked_state( - fuchsia::accessibility::semantics::CheckedState::MIXED); + states.checked_state( + fuchsia_accessibility_semantics::CheckedState::kMixed); break; } } @@ -213,23 +210,23 @@ // Indicates whether a node has been selected. if (GetData().IsSelectable() && HasBoolAttribute(ax::mojom::BoolAttribute::kSelected)) { - states.set_selected(GetBoolAttribute(ax::mojom::BoolAttribute::kSelected)); + states.selected(GetBoolAttribute(ax::mojom::BoolAttribute::kSelected)); } // Indicates if the node is hidden. - states.set_hidden(IsInvisibleOrIgnored()); + states.hidden(IsInvisibleOrIgnored()); // The user entered value of the node, if applicable. if (HasStringAttribute(ax::mojom::StringAttribute::kValue)) { const std::string& value = GetStringAttribute(ax::mojom::StringAttribute::kValue); - states.set_value( - value.substr(0, fuchsia::accessibility::semantics::MAX_LABEL_SIZE)); + states.value( + value.substr(0, fuchsia_accessibility_semantics::kMaxLabelSize)); } // The value a range element currently has. if (HasFloatAttribute(ax::mojom::FloatAttribute::kValueForRange)) { - states.set_range_value( + states.range_value( GetFloatAttribute(ax::mojom::FloatAttribute::kValueForRange)); } @@ -239,133 +236,136 @@ const float y_scroll_offset = GetIntAttribute(ax::mojom::IntAttribute::kScrollY); if (x_scroll_offset || y_scroll_offset) - states.set_viewport_offset({x_scroll_offset, y_scroll_offset}); + states.viewport_offset({{x_scroll_offset, y_scroll_offset}}); if (IsFocusable()) - states.set_focusable(true); + states.focusable(true); - states.set_has_input_focus(IsFocused()); + states.has_input_focus(IsFocused()); return states; } -fuchsia::accessibility::semantics::Attributes +fuchsia_accessibility_semantics::Attributes BrowserAccessibilityFuchsia::GetFuchsiaAttributes() const { - fuchsia::accessibility::semantics::Attributes attributes; + fuchsia_accessibility_semantics::Attributes attributes; if (HasStringAttribute(ax::mojom::StringAttribute::kName)) { const std::string& name = GetStringAttribute(ax::mojom::StringAttribute::kName); - attributes.set_label( - name.substr(0, fuchsia::accessibility::semantics::MAX_LABEL_SIZE)); + attributes.label( + name.substr(0, fuchsia_accessibility_semantics::kMaxLabelSize)); } if (HasStringAttribute(ax::mojom::StringAttribute::kDescription)) { const std::string& description = GetStringAttribute(ax::mojom::StringAttribute::kDescription); - attributes.set_secondary_label(description.substr( - 0, fuchsia::accessibility::semantics::MAX_LABEL_SIZE)); + attributes.secondary_label( + description.substr(0, fuchsia_accessibility_semantics::kMaxLabelSize)); } if (GetData().IsRangeValueSupported()) { - fuchsia::accessibility::semantics::RangeAttributes range_attributes; + fuchsia_accessibility_semantics::RangeAttributes range_attributes; if (HasFloatAttribute(ax::mojom::FloatAttribute::kMinValueForRange)) { - range_attributes.set_min_value( + range_attributes.min_value( GetFloatAttribute(ax::mojom::FloatAttribute::kMinValueForRange)); } if (HasFloatAttribute(ax::mojom::FloatAttribute::kMaxValueForRange)) { - range_attributes.set_max_value( + range_attributes.max_value( GetFloatAttribute(ax::mojom::FloatAttribute::kMaxValueForRange)); } if (HasFloatAttribute(ax::mojom::FloatAttribute::kStepValueForRange)) { - range_attributes.set_step_delta( + range_attributes.step_delta( GetFloatAttribute(ax::mojom::FloatAttribute::kStepValueForRange)); } - attributes.set_range(std::move(range_attributes)); + attributes.range(std::move(range_attributes)); } if (IsTable()) { - fuchsia::accessibility::semantics::TableAttributes table_attributes; + fuchsia_accessibility_semantics::TableAttributes table_attributes; auto col_count = GetTableColCount(); if (col_count) - table_attributes.set_number_of_columns(*col_count); + table_attributes.number_of_columns(*col_count); auto row_count = GetTableRowCount(); if (row_count) - table_attributes.set_number_of_rows(*row_count); + table_attributes.number_of_rows(*row_count); if (!table_attributes.IsEmpty()) - attributes.set_table_attributes(std::move(table_attributes)); + attributes.table_attributes(std::move(table_attributes)); } if (IsTableRow()) { - fuchsia::accessibility::semantics::TableRowAttributes table_row_attributes; + fuchsia_accessibility_semantics::TableRowAttributes table_row_attributes; auto row_index = GetTableRowRowIndex(); if (row_index) { - table_row_attributes.set_row_index(*row_index); - attributes.set_table_row_attributes(std::move(table_row_attributes)); + table_row_attributes.row_index(*row_index); + attributes.table_row_attributes(std::move(table_row_attributes)); } } if (IsTableCellOrHeader()) { - fuchsia::accessibility::semantics::TableCellAttributes - table_cell_attributes; + fuchsia_accessibility_semantics::TableCellAttributes table_cell_attributes; auto col_index = GetTableCellColIndex(); if (col_index) - table_cell_attributes.set_column_index(*col_index); + table_cell_attributes.column_index(*col_index); auto row_index = GetTableCellRowIndex(); if (row_index) - table_cell_attributes.set_row_index(*row_index); + table_cell_attributes.row_index(*row_index); auto col_span = GetTableCellColSpan(); if (col_span) - table_cell_attributes.set_column_span(*col_span); + table_cell_attributes.column_span(*col_span); auto row_span = GetTableCellRowSpan(); if (row_span) - table_cell_attributes.set_row_span(*row_span); + table_cell_attributes.row_span(*row_span); if (!table_cell_attributes.IsEmpty()) - attributes.set_table_cell_attributes(std::move(table_cell_attributes)); + attributes.table_cell_attributes(std::move(table_cell_attributes)); } if (IsList()) { absl::optional<int> size = GetSetSize(); if (size) { - fuchsia::accessibility::semantics::SetAttributes list_attributes; - list_attributes.set_size(*size); - attributes.set_list_attributes(std::move(list_attributes)); + fuchsia_accessibility_semantics::SetAttributes list_attributes; + list_attributes.size(*size); + attributes.list_attributes(std::move(list_attributes)); } } if (IsListElement()) { absl::optional<int> index = GetPosInSet(); if (index) { - fuchsia::accessibility::semantics::SetAttributes list_element_attributes; - list_element_attributes.set_index(*index); - attributes.set_list_element_attributes( - std::move(list_element_attributes)); + fuchsia_accessibility_semantics::SetAttributes list_element_attributes; + list_element_attributes.index(*index); + attributes.list_element_attributes(std::move(list_element_attributes)); } } return attributes; } -fuchsia::ui::gfx::BoundingBox BrowserAccessibilityFuchsia::GetFuchsiaLocation() +fuchsia_ui_gfx::BoundingBox BrowserAccessibilityFuchsia::GetFuchsiaLocation() const { const gfx::RectF& bounds = GetLocation(); - fuchsia::ui::gfx::BoundingBox box; - // Since the origin is at the top left, min should represent the top left and - // max should be the bottom right. - box.min = scenic::NewVector3({bounds.x(), bounds.y(), 0.0f}); - box.max = scenic::NewVector3({bounds.right(), bounds.bottom(), 0.0f}); - return box; + return {{ + .min = {{ + .x = bounds.x(), + .y = bounds.y(), + .z = 0.0f, + }}, + .max = {{ + .x = bounds.right(), + .y = bounds.bottom(), + .z = 0.0f, + }}, + }}; } -fuchsia::ui::gfx::mat4 BrowserAccessibilityFuchsia::GetFuchsiaTransform() - const { +fuchsia_ui_gfx::Mat4 BrowserAccessibilityFuchsia::GetFuchsiaTransform() const { // Get AXNode's explicit transform. gfx::Transform transform; if (GetData().relative_bounds.transform) @@ -374,9 +374,7 @@ // Convert to fuchsia's transform type. std::array<float, 16> mat = {}; transform.GetColMajorF(mat.data()); - fuchsia::ui::gfx::Matrix4Value fuchsia_transform = - scenic::NewMatrix4Value(mat); - return fuchsia_transform.value; + return {{.matrix = mat}}; } uint32_t BrowserAccessibilityFuchsia::GetOffsetContainerOrRootNodeID() const { @@ -405,7 +403,8 @@ if (!GetAccessibilityBridge()) return; - GetAccessibilityBridge()->UpdateNode(ToFuchsiaNodeData()); + GetAccessibilityBridge()->UpdateNode( + fidl::NaturalToHLCPP(ToFuchsiaNodeData())); } void BrowserAccessibilityFuchsia::DeleteNode() {
diff --git a/content/browser/accessibility/browser_accessibility_fuchsia.h b/content/browser/accessibility/browser_accessibility_fuchsia.h index 347ff00..b069f72 100644 --- a/content/browser/accessibility/browser_accessibility_fuchsia.h +++ b/content/browser/accessibility/browser_accessibility_fuchsia.h
@@ -5,7 +5,7 @@ #ifndef CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_FUCHSIA_H_ #define CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_FUCHSIA_H_ -#include <fuchsia/accessibility/semantics/cpp/fidl.h> +#include <fidl/fuchsia.accessibility.semantics/cpp/fidl.h> #include <vector> @@ -36,7 +36,7 @@ // Returns the fuchsia representation of the AXNode to which this // BrowserAccessibility object refers. - fuchsia::accessibility::semantics::Node ToFuchsiaNodeData() const; + fuchsia_accessibility_semantics::Node ToFuchsiaNodeData() const; // Returns the fuchsia ID of this node's offset container if the offset // container ID is valid. Otherwise, returns the ID of this tree's root node. @@ -58,13 +58,13 @@ void UpdateNode(); void DeleteNode(); - std::vector<fuchsia::accessibility::semantics::Action> GetFuchsiaActions() + std::vector<fuchsia_accessibility_semantics::Action> GetFuchsiaActions() const; - fuchsia::accessibility::semantics::Role GetFuchsiaRole() const; - fuchsia::accessibility::semantics::States GetFuchsiaStates() const; - fuchsia::accessibility::semantics::Attributes GetFuchsiaAttributes() const; - fuchsia::ui::gfx::BoundingBox GetFuchsiaLocation() const; - fuchsia::ui::gfx::mat4 GetFuchsiaTransform() const; + fuchsia_accessibility_semantics::Role GetFuchsiaRole() const; + fuchsia_accessibility_semantics::States GetFuchsiaStates() const; + fuchsia_accessibility_semantics::Attributes GetFuchsiaAttributes() const; + fuchsia_ui_gfx::BoundingBox GetFuchsiaLocation() const; + fuchsia_ui_gfx::Mat4 GetFuchsiaTransform() const; std::vector<uint32_t> GetFuchsiaChildIDs() const; // Returns true if this AXNode has role AXRole::kList.
diff --git a/content/browser/accessibility/browser_accessibility_fuchsia_unittest.cc b/content/browser/accessibility/browser_accessibility_fuchsia_unittest.cc index 29e7667f3..eb1bffa 100644 --- a/content/browser/accessibility/browser_accessibility_fuchsia_unittest.cc +++ b/content/browser/accessibility/browser_accessibility_fuchsia_unittest.cc
@@ -4,7 +4,7 @@ #include "content/browser/accessibility/browser_accessibility_fuchsia.h" -#include <fuchsia/accessibility/semantics/cpp/fidl.h> +#include <fidl/fuchsia.accessibility.semantics/cpp/fidl.h> #include <lib/ui/scenic/cpp/commands.h> #include <map> @@ -20,7 +20,7 @@ namespace { using AXRole = ax::mojom::Role; -using fuchsia::accessibility::semantics::Role; +using fuchsia_accessibility_semantics::Role; constexpr int32_t kRootId = 182; constexpr int32_t kRowNodeId1 = 2; @@ -141,28 +141,28 @@ } TEST_F(BrowserAccessibilityFuchsiaTest, ToFuchsiaNodeDataTranslatesRoles) { - std::map<ax::mojom::Role, fuchsia::accessibility::semantics::Role> + std::map<ax::mojom::Role, fuchsia_accessibility_semantics::Role> role_mapping = { - {AXRole::kButton, Role::BUTTON}, - {AXRole::kCheckBox, Role::CHECK_BOX}, - {AXRole::kHeader, Role::HEADER}, - {AXRole::kImage, Role::IMAGE}, - {AXRole::kLink, Role::LINK}, - {AXRole::kList, Role::LIST}, - {AXRole::kListItem, Role::LIST_ELEMENT}, - {AXRole::kListMarker, Role::LIST_ELEMENT_MARKER}, - {AXRole::kRadioButton, Role::RADIO_BUTTON}, - {AXRole::kSlider, Role::SLIDER}, - {AXRole::kTextField, Role::TEXT_FIELD}, - {AXRole::kSearchBox, Role::SEARCH_BOX}, - {AXRole::kTextFieldWithComboBox, Role::TEXT_FIELD_WITH_COMBO_BOX}, - {AXRole::kTable, Role::TABLE}, - {AXRole::kGrid, Role::GRID}, - {AXRole::kRow, Role::TABLE_ROW}, - {AXRole::kCell, Role::CELL}, - {AXRole::kColumnHeader, Role::COLUMN_HEADER}, - {AXRole::kRowGroup, Role::ROW_GROUP}, - {AXRole::kParagraph, Role::PARAGRAPH}}; + {AXRole::kButton, Role::kButton}, + {AXRole::kCheckBox, Role::kCheckBox}, + {AXRole::kHeader, Role::kHeader}, + {AXRole::kImage, Role::kImage}, + {AXRole::kLink, Role::kLink}, + {AXRole::kList, Role::kList}, + {AXRole::kListItem, Role::kListElement}, + {AXRole::kListMarker, Role::kListElementMarker}, + {AXRole::kRadioButton, Role::kRadioButton}, + {AXRole::kSlider, Role::kSlider}, + {AXRole::kTextField, Role::kTextField}, + {AXRole::kSearchBox, Role::kSearchBox}, + {AXRole::kTextFieldWithComboBox, Role::kTextFieldWithComboBox}, + {AXRole::kTable, Role::kTable}, + {AXRole::kGrid, Role::kGrid}, + {AXRole::kRow, Role::kTableRow}, + {AXRole::kCell, Role::kCell}, + {AXRole::kColumnHeader, Role::kColumnHeader}, + {AXRole::kRowGroup, Role::kRowGroup}, + {AXRole::kParagraph, Role::kParagraph}}; for (const auto& role_pair : role_mapping) { ui::AXNodeData node; @@ -186,16 +186,16 @@ TEST_F(BrowserAccessibilityFuchsiaTest, ToFuchsiaNodeDataTranslatesNodeActions) { - std::map<ax::mojom::Action, fuchsia::accessibility::semantics::Action> + std::map<ax::mojom::Action, fuchsia_accessibility_semantics::Action> action_mapping = { {ax::mojom::Action::kDoDefault, - fuchsia::accessibility::semantics::Action::DEFAULT}, + fuchsia_accessibility_semantics::Action::kDefault}, {ax::mojom::Action::kFocus, - fuchsia::accessibility::semantics::Action::SET_FOCUS}, + fuchsia_accessibility_semantics::Action::kSetFocus}, {ax::mojom::Action::kSetValue, - fuchsia::accessibility::semantics::Action::SET_VALUE}, + fuchsia_accessibility_semantics::Action::kSetValue}, {ax::mojom::Action::kScrollToMakeVisible, - fuchsia::accessibility::semantics::Action::SHOW_ON_SCREEN}}; + fuchsia_accessibility_semantics::Action::kShowOnScreen}}; for (const auto& action_pair : action_mapping) { ui::AXNodeData node; @@ -213,7 +213,7 @@ ASSERT_TRUE(browser_accessibility_fuchsia); auto fuchsia_node_data = browser_accessibility_fuchsia->ToFuchsiaNodeData(); - const auto& actions = fuchsia_node_data.actions(); + const auto& actions = fuchsia_node_data.actions().value(); ASSERT_EQ(actions.size(), 1u); EXPECT_EQ(actions[0], action_pair.second); } @@ -240,10 +240,11 @@ ASSERT_TRUE(browser_accessibility_fuchsia); auto fuchsia_node_data = browser_accessibility_fuchsia->ToFuchsiaNodeData(); - ASSERT_TRUE(fuchsia_node_data.has_attributes()); - ASSERT_TRUE(fuchsia_node_data.attributes().has_label()); - EXPECT_EQ(fuchsia_node_data.attributes().label(), kLabel); - EXPECT_EQ(fuchsia_node_data.attributes().secondary_label(), kSecondaryLabel); + ASSERT_TRUE(fuchsia_node_data.attributes().has_value()); + ASSERT_TRUE(fuchsia_node_data.attributes()->label().has_value()); + EXPECT_EQ(fuchsia_node_data.attributes()->label().value(), kLabel); + EXPECT_EQ(fuchsia_node_data.attributes()->secondary_label().value(), + kSecondaryLabel); } TEST_F(BrowserAccessibilityFuchsiaTest, @@ -272,15 +273,16 @@ ASSERT_TRUE(browser_accessibility_fuchsia); auto fuchsia_node_data = browser_accessibility_fuchsia->ToFuchsiaNodeData(); - ASSERT_TRUE(fuchsia_node_data.has_attributes()); - ASSERT_TRUE(fuchsia_node_data.attributes().has_range()); - const auto& range_attributes = fuchsia_node_data.attributes().range(); + ASSERT_TRUE(fuchsia_node_data.attributes().has_value()); + ASSERT_TRUE(fuchsia_node_data.attributes()->range().has_value()); + const auto& range_attributes = + fuchsia_node_data.attributes()->range().value(); EXPECT_EQ(range_attributes.min_value(), kMin); EXPECT_EQ(range_attributes.max_value(), kMax); EXPECT_EQ(range_attributes.step_delta(), kStep); - EXPECT_TRUE(fuchsia_node_data.has_states()); - EXPECT_TRUE(fuchsia_node_data.states().has_range_value()); - EXPECT_EQ(fuchsia_node_data.states().range_value(), kValue); + EXPECT_TRUE(fuchsia_node_data.states()); + EXPECT_TRUE(fuchsia_node_data.states()->range_value().has_value()); + EXPECT_EQ(fuchsia_node_data.states()->range_value().value(), kValue); } TEST_F(BrowserAccessibilityFuchsiaTest, @@ -298,13 +300,14 @@ auto fuchsia_node_data = browser_accessibility_fuchsia->ToFuchsiaNodeData(); EXPECT_EQ(fuchsia_node_data.role(), - fuchsia::accessibility::semantics::Role::TABLE); - ASSERT_TRUE(fuchsia_node_data.has_attributes()); - ASSERT_TRUE(fuchsia_node_data.attributes().has_table_attributes()); + fuchsia_accessibility_semantics::Role::kTable); + ASSERT_TRUE(fuchsia_node_data.attributes().has_value()); + ASSERT_TRUE(fuchsia_node_data.attributes()->table_attributes().has_value()); EXPECT_EQ( - fuchsia_node_data.attributes().table_attributes().number_of_rows(), 2u); + fuchsia_node_data.attributes()->table_attributes()->number_of_rows(), + 2u); EXPECT_EQ( - fuchsia_node_data.attributes().table_attributes().number_of_columns(), + fuchsia_node_data.attributes()->table_attributes()->number_of_columns(), 2u); } @@ -317,11 +320,13 @@ auto fuchsia_node_data = browser_accessibility_fuchsia->ToFuchsiaNodeData(); EXPECT_EQ(fuchsia_node_data.role(), - fuchsia::accessibility::semantics::Role::TABLE_ROW); - ASSERT_TRUE(fuchsia_node_data.has_attributes()); - ASSERT_TRUE(fuchsia_node_data.attributes().has_table_row_attributes()); - EXPECT_EQ(fuchsia_node_data.attributes().table_row_attributes().row_index(), - 1u); + fuchsia_accessibility_semantics::Role::kTableRow); + ASSERT_TRUE(fuchsia_node_data.attributes().has_value()); + ASSERT_TRUE( + fuchsia_node_data.attributes()->table_row_attributes().has_value()); + EXPECT_EQ( + fuchsia_node_data.attributes()->table_row_attributes()->row_index(), + 1u); } // Verify table cell node translation. @@ -333,13 +338,15 @@ auto fuchsia_node_data = browser_accessibility_fuchsia->ToFuchsiaNodeData(); EXPECT_EQ(fuchsia_node_data.role(), - fuchsia::accessibility::semantics::Role::CELL); - ASSERT_TRUE(fuchsia_node_data.has_attributes()); - ASSERT_TRUE(fuchsia_node_data.attributes().has_table_cell_attributes()); + fuchsia_accessibility_semantics::Role::kCell); + ASSERT_TRUE(fuchsia_node_data.attributes().has_value()); + ASSERT_TRUE( + fuchsia_node_data.attributes()->table_cell_attributes().has_value()); EXPECT_EQ( - fuchsia_node_data.attributes().table_cell_attributes().row_index(), 1u); + fuchsia_node_data.attributes()->table_cell_attributes()->row_index(), + 1u); EXPECT_EQ( - fuchsia_node_data.attributes().table_cell_attributes().column_index(), + fuchsia_node_data.attributes()->table_cell_attributes()->column_index(), 1u); } } @@ -357,11 +364,12 @@ ASSERT_TRUE(browser_accessibility_fuchsia); auto fuchsia_node_data = browser_accessibility_fuchsia->ToFuchsiaNodeData(); EXPECT_EQ(fuchsia_node_data.role(), - fuchsia::accessibility::semantics::Role::LIST); - ASSERT_TRUE(fuchsia_node_data.has_attributes()); - ASSERT_TRUE(fuchsia_node_data.attributes().has_list_attributes()); - ASSERT_FALSE(fuchsia_node_data.attributes().has_list_element_attributes()); - EXPECT_EQ(fuchsia_node_data.attributes().list_attributes().size(), 2u); + fuchsia_accessibility_semantics::Role::kList); + ASSERT_TRUE(fuchsia_node_data.attributes().has_value()); + ASSERT_TRUE(fuchsia_node_data.attributes()->list_attributes().has_value()); + ASSERT_FALSE( + fuchsia_node_data.attributes()->list_element_attributes().has_value()); + EXPECT_EQ(fuchsia_node_data.attributes()->list_attributes()->size(), 2u); } // Verify that the list elements were translated. @@ -371,28 +379,29 @@ ASSERT_TRUE(browser_accessibility_fuchsia); auto fuchsia_node_data = browser_accessibility_fuchsia->ToFuchsiaNodeData(); EXPECT_EQ(fuchsia_node_data.role(), - fuchsia::accessibility::semantics::Role::LIST_ELEMENT); - ASSERT_TRUE(fuchsia_node_data.has_attributes()); - ASSERT_FALSE(fuchsia_node_data.attributes().has_list_attributes()); - ASSERT_TRUE(fuchsia_node_data.attributes().has_list_element_attributes()); - EXPECT_EQ(fuchsia_node_data.attributes().list_element_attributes().index(), - 2u); + fuchsia_accessibility_semantics::Role::kListElement); + ASSERT_TRUE(fuchsia_node_data.attributes().has_value()); + ASSERT_FALSE(fuchsia_node_data.attributes()->list_attributes().has_value()); + ASSERT_TRUE( + fuchsia_node_data.attributes()->list_element_attributes().has_value()); + EXPECT_EQ( + fuchsia_node_data.attributes()->list_element_attributes()->index(), 2u); } } TEST_F(BrowserAccessibilityFuchsiaTest, ToFuchsiaNodeDataTranslatesCheckedState) { std::map<ax::mojom::CheckedState, - fuchsia::accessibility::semantics::CheckedState> + fuchsia_accessibility_semantics::CheckedState> state_mapping = { {ax::mojom::CheckedState::kNone, - fuchsia::accessibility::semantics::CheckedState::NONE}, + fuchsia_accessibility_semantics::CheckedState::kNone}, {ax::mojom::CheckedState::kTrue, - fuchsia::accessibility::semantics::CheckedState::CHECKED}, + fuchsia_accessibility_semantics::CheckedState::kChecked}, {ax::mojom::CheckedState::kFalse, - fuchsia::accessibility::semantics::CheckedState::UNCHECKED}, + fuchsia_accessibility_semantics::CheckedState::kUnchecked}, {ax::mojom::CheckedState::kMixed, - fuchsia::accessibility::semantics::CheckedState::MIXED}}; + fuchsia_accessibility_semantics::CheckedState::kMixed}}; for (const auto state_pair : state_mapping) { ui::AXNodeData node; @@ -411,9 +420,10 @@ ASSERT_TRUE(browser_accessibility_fuchsia); auto fuchsia_node_data = browser_accessibility_fuchsia->ToFuchsiaNodeData(); - ASSERT_TRUE(fuchsia_node_data.has_states()); - ASSERT_TRUE(fuchsia_node_data.states().has_checked_state()); - EXPECT_EQ(fuchsia_node_data.states().checked_state(), state_pair.second); + ASSERT_TRUE(fuchsia_node_data.states().has_value()); + ASSERT_TRUE(fuchsia_node_data.states()->checked_state().has_value()); + EXPECT_EQ(fuchsia_node_data.states()->checked_state().value(), + state_pair.second); } } @@ -434,9 +444,9 @@ ASSERT_TRUE(browser_accessibility_fuchsia); auto fuchsia_node_data = browser_accessibility_fuchsia->ToFuchsiaNodeData(); - ASSERT_TRUE(fuchsia_node_data.has_states()); - EXPECT_TRUE(fuchsia_node_data.states().has_selected()); - EXPECT_TRUE(fuchsia_node_data.states().selected()); + ASSERT_TRUE(fuchsia_node_data.states().has_value()); + EXPECT_TRUE(fuchsia_node_data.states()->selected().has_value()); + EXPECT_TRUE(fuchsia_node_data.states()->selected().value()); } TEST_F(BrowserAccessibilityFuchsiaTest, @@ -456,9 +466,9 @@ ASSERT_TRUE(browser_accessibility_fuchsia); auto fuchsia_node_data = browser_accessibility_fuchsia->ToFuchsiaNodeData(); - ASSERT_TRUE(fuchsia_node_data.has_states()); - ASSERT_TRUE(fuchsia_node_data.states().has_hidden()); - EXPECT_TRUE(fuchsia_node_data.states().hidden()); + ASSERT_TRUE(fuchsia_node_data.states().has_value()); + ASSERT_TRUE(fuchsia_node_data.states()->hidden().has_value()); + EXPECT_TRUE(fuchsia_node_data.states()->hidden().value()); } TEST_F(BrowserAccessibilityFuchsiaTest, @@ -478,16 +488,16 @@ ASSERT_TRUE(browser_accessibility_fuchsia); auto fuchsia_node_data = browser_accessibility_fuchsia->ToFuchsiaNodeData(); - ASSERT_TRUE(fuchsia_node_data.has_states()); - ASSERT_TRUE(fuchsia_node_data.states().has_hidden()); - EXPECT_TRUE(fuchsia_node_data.states().hidden()); + ASSERT_TRUE(fuchsia_node_data.states().has_value()); + ASSERT_TRUE(fuchsia_node_data.states()->hidden().has_value()); + EXPECT_TRUE(fuchsia_node_data.states()->hidden().value()); } TEST_F(BrowserAccessibilityFuchsiaTest, ToFuchsiaNodeDataTranslatesValue) { const auto full_value = - std::string(fuchsia::accessibility::semantics::MAX_LABEL_SIZE + 1, 'a'); + std::string(fuchsia_accessibility_semantics::kMaxLabelSize + 1, 'a'); const auto truncated_value = - std::string(fuchsia::accessibility::semantics::MAX_LABEL_SIZE, 'a'); + std::string(fuchsia_accessibility_semantics::kMaxLabelSize, 'a'); ui::AXNodeData node; node.id = kRootId; @@ -504,9 +514,8 @@ ASSERT_TRUE(browser_accessibility_fuchsia); auto fuchsia_node_data = browser_accessibility_fuchsia->ToFuchsiaNodeData(); - ASSERT_TRUE(fuchsia_node_data.has_states()); ASSERT_TRUE(fuchsia_node_data.states().has_value()); - EXPECT_EQ(fuchsia_node_data.states().value(), truncated_value); + EXPECT_EQ(fuchsia_node_data.states()->value(), truncated_value); } TEST_F(BrowserAccessibilityFuchsiaTest, @@ -530,11 +539,12 @@ ASSERT_TRUE(browser_accessibility_fuchsia); auto fuchsia_node_data = browser_accessibility_fuchsia->ToFuchsiaNodeData(); - ASSERT_TRUE(fuchsia_node_data.has_states()); - ASSERT_TRUE(fuchsia_node_data.states().has_viewport_offset()); - const auto& viewport_offset = fuchsia_node_data.states().viewport_offset(); - EXPECT_EQ(viewport_offset.x, kScrollX); - EXPECT_EQ(viewport_offset.y, kScrollY); + ASSERT_TRUE(fuchsia_node_data.states().has_value()); + ASSERT_TRUE(fuchsia_node_data.states()->viewport_offset().has_value()); + const auto& viewport_offset = + fuchsia_node_data.states()->viewport_offset().value(); + EXPECT_EQ(viewport_offset.x(), kScrollX); + EXPECT_EQ(viewport_offset.y(), kScrollY); } TEST_F(BrowserAccessibilityFuchsiaTest, @@ -570,21 +580,21 @@ ASSERT_TRUE(browser_accessibility_fuchsia); auto fuchsia_node_data = browser_accessibility_fuchsia->ToFuchsiaNodeData(); - ASSERT_TRUE(fuchsia_node_data.has_node_to_container_transform()); + ASSERT_TRUE(fuchsia_node_data.node_to_container_transform().has_value()); const auto& transform = - fuchsia_node_data.node_to_container_transform().matrix; + fuchsia_node_data.node_to_container_transform()->matrix(); EXPECT_EQ(transform[0], x_scale); EXPECT_EQ(transform[5], y_scale); EXPECT_EQ(transform[10], z_scale); EXPECT_EQ(transform[12], x_translation); EXPECT_EQ(transform[13], y_translation); EXPECT_EQ(transform[14], z_translation); - ASSERT_TRUE(fuchsia_node_data.has_location()); - const auto& location = fuchsia_node_data.location(); - EXPECT_EQ(location.min.x, x_min); - EXPECT_EQ(location.min.y, y_min); - EXPECT_EQ(location.max.x, x_max); - EXPECT_EQ(location.max.y, y_max); + ASSERT_TRUE(fuchsia_node_data.location().has_value()); + const auto& location = fuchsia_node_data.location().value(); + EXPECT_EQ(location.min().x(), x_min); + EXPECT_EQ(location.min().y(), y_min); + EXPECT_EQ(location.max().x(), x_max); + EXPECT_EQ(location.max().y(), y_max); } TEST_F(BrowserAccessibilityFuchsiaTest, @@ -611,16 +621,17 @@ ToBrowserAccessibilityFuchsia(manager->GetFromID(2)); ASSERT_TRUE(child); auto child_node_data = child->ToFuchsiaNodeData(); - ASSERT_TRUE(child_node_data.has_container_id()); - EXPECT_EQ(child_node_data.container_id(), root->GetFuchsiaNodeID()); + ASSERT_TRUE(child_node_data.container_id().has_value()); + EXPECT_EQ(child_node_data.container_id().value(), root->GetFuchsiaNodeID()); // Verify that node 3's offset container was translated correctly. BrowserAccessibilityFuchsia* grandchild = ToBrowserAccessibilityFuchsia(manager->GetFromID(3)); ASSERT_TRUE(grandchild); auto grandchild_node_data = grandchild->ToFuchsiaNodeData(); - ASSERT_TRUE(grandchild_node_data.has_container_id()); - EXPECT_EQ(grandchild_node_data.container_id(), child->GetFuchsiaNodeID()); + ASSERT_TRUE(grandchild_node_data.container_id().has_value()); + EXPECT_EQ(grandchild_node_data.container_id().value(), + child->GetFuchsiaNodeID()); } TEST_F(BrowserAccessibilityFuchsiaTest, @@ -641,10 +652,10 @@ ToBrowserAccessibilityFuchsia(manager->GetFromID(2)); ASSERT_TRUE(child); auto child_node_data = child->ToFuchsiaNodeData(); - ASSERT_TRUE(child_node_data.has_container_id()); + ASSERT_TRUE(child_node_data.container_id().has_value()); // Offset container ID should default to 0 if the specified node doesn't // exist. - EXPECT_EQ(child_node_data.container_id(), 0u); + EXPECT_EQ(child_node_data.container_id().value(), 0u); } TEST_F(BrowserAccessibilityFuchsiaTest, @@ -677,9 +688,11 @@ ASSERT_TRUE(child_1); ASSERT_TRUE(child_2); EXPECT_EQ(fuchsia_node_data.node_id(), root->GetFuchsiaNodeID()); - ASSERT_EQ(fuchsia_node_data.child_ids().size(), 2u); - EXPECT_EQ(fuchsia_node_data.child_ids()[0], child_1->GetFuchsiaNodeID()); - EXPECT_EQ(fuchsia_node_data.child_ids()[1], child_2->GetFuchsiaNodeID()); + ASSERT_EQ(fuchsia_node_data.child_ids()->size(), 2u); + EXPECT_EQ(fuchsia_node_data.child_ids().value()[0], + child_1->GetFuchsiaNodeID()); + EXPECT_EQ(fuchsia_node_data.child_ids().value()[1], + child_2->GetFuchsiaNodeID()); } TEST_F(BrowserAccessibilityFuchsiaTest, ChildTree) { @@ -717,7 +730,7 @@ { ASSERT_TRUE(browser_accessibility_fuchsia); - fuchsia::accessibility::semantics::Node fuchsia_node_data = + fuchsia_accessibility_semantics::Node fuchsia_node_data = browser_accessibility_fuchsia->ToFuchsiaNodeData(); // Get the root of the child tree to verify that it's present in the parent @@ -725,8 +738,9 @@ BrowserAccessibilityFuchsia* child_root = ToBrowserAccessibilityFuchsia( child_manager->GetBrowserAccessibilityRoot()); - ASSERT_EQ(fuchsia_node_data.child_ids().size(), 1u); - EXPECT_EQ(fuchsia_node_data.child_ids()[0], child_root->GetFuchsiaNodeID()); + ASSERT_EQ(fuchsia_node_data.child_ids()->size(), 1u); + EXPECT_EQ(fuchsia_node_data.child_ids().value()[0], + child_root->GetFuchsiaNodeID()); } // Destroy the child tree, and ensure that the parent fuchsia node's child IDs @@ -735,10 +749,10 @@ { ASSERT_TRUE(browser_accessibility_fuchsia); - fuchsia::accessibility::semantics::Node fuchsia_node_data = + fuchsia_accessibility_semantics::Node fuchsia_node_data = browser_accessibility_fuchsia->ToFuchsiaNodeData(); - EXPECT_TRUE(fuchsia_node_data.child_ids().empty()); + EXPECT_TRUE(fuchsia_node_data.child_ids()->empty()); } } @@ -762,7 +776,7 @@ ASSERT_TRUE(browser_accessibility_fuchsia); auto fuchsia_node_data = browser_accessibility_fuchsia->ToFuchsiaNodeData(); - EXPECT_TRUE(fuchsia_node_data.child_ids().empty()); + EXPECT_TRUE(fuchsia_node_data.child_ids()->empty()); } TEST_F(BrowserAccessibilityFuchsiaTest, GetFuchsiaNodeIDNonRootTree) {
diff --git a/content/browser/attribution_reporting/attribution_beacon_id.h b/content/browser/attribution_reporting/attribution_beacon_id.h index ad1aa7a5..f41cd77 100644 --- a/content/browser/attribution_reporting/attribution_beacon_id.h +++ b/content/browser/attribution_reporting/attribution_beacon_id.h
@@ -8,14 +8,10 @@ #include <stdint.h> #include "base/types/strong_alias.h" -#include "third_party/abseil-cpp/absl/types/variant.h" namespace content { -using EventBeaconId = base::StrongAlias<struct EventBeaconTag, int64_t>; -using NavigationBeaconId = - base::StrongAlias<struct NavigationBeaconTag, int64_t>; -using BeaconId = absl::variant<EventBeaconId, NavigationBeaconId>; +using BeaconId = base::StrongAlias<struct BeaconTag, int64_t>; } // namespace content
diff --git a/content/browser/attribution_reporting/attribution_data_host_manager.h b/content/browser/attribution_reporting/attribution_data_host_manager.h index e2f4cae..75f1b36 100644 --- a/content/browser/attribution_reporting/attribution_data_host_manager.h +++ b/content/browser/attribution_reporting/attribution_data_host_manager.h
@@ -5,10 +5,13 @@ #ifndef CONTENT_BROWSER_ATTRIBUTION_REPORTING_ATTRIBUTION_DATA_HOST_MANAGER_H_ #define CONTENT_BROWSER_ATTRIBUTION_REPORTING_ATTRIBUTION_DATA_HOST_MANAGER_H_ +#include <stdint.h> + #include "base/memory/weak_ptr.h" #include "components/attribution_reporting/registration_type.mojom-forward.h" #include "content/browser/attribution_reporting/attribution_beacon_id.h" #include "mojo/public/cpp/bindings/pending_receiver.h" +#include "third_party/abseil-cpp/absl/types/optional.h" #include "third_party/blink/public/common/tokens/tokens.h" #include "third_party/blink/public/mojom/conversions/attribution_data_host.mojom-forward.h" #include "third_party/blink/public/mojom/conversions/attribution_reporting.mojom-forward.h" @@ -94,8 +97,11 @@ // RFHI was destroyed, therefore we need to store the information for later // use. Passes the topmost ancestor of the initiator render frame for // obtaining the page access report. + // `navigation_id` is the id of the navigation for automatic beacons and + // `absl::nullopt` for event beacons. virtual void NotifyFencedFrameReportingBeaconStarted( BeaconId beacon_id, + absl::optional<int64_t> navigation_id, attribution_reporting::SuitableOrigin source_origin, bool is_within_fenced_frame, AttributionInputEvent input_event,
diff --git a/content/browser/attribution_reporting/attribution_data_host_manager_impl.cc b/content/browser/attribution_reporting/attribution_data_host_manager_impl.cc index d785a21..db77edfe1 100644 --- a/content/browser/attribution_reporting/attribution_data_host_manager_impl.cc +++ b/content/browser/attribution_reporting/attribution_data_host_manager_impl.cc
@@ -37,6 +37,7 @@ #include "components/attribution_reporting/trigger_registration.h" #include "content/browser/attribution_reporting/attribution_beacon_id.h" #include "content/browser/attribution_reporting/attribution_constants.h" +#include "content/browser/attribution_reporting/attribution_features.h" #include "content/browser/attribution_reporting/attribution_input_event.h" #include "content/browser/attribution_reporting/attribution_manager.h" #include "content/browser/attribution_reporting/attribution_trigger.h" @@ -235,7 +236,12 @@ AttributionNavigationType nav_type; }; - using Data = absl::variant<NavigationRedirect, BeaconId>; + struct Beacon { + BeaconId id; + absl::optional<int64_t> navigation_id; + }; + + using Data = absl::variant<NavigationRedirect, Beacon>; SourceRegistrations(SuitableOrigin source_origin, bool is_within_fenced_frame, @@ -302,8 +308,8 @@ [](const NavigationRedirect& redirect) { return SourceRegistrationsId(redirect.attribution_src_token); }, - [](const BeaconId& beacon_id) { - return SourceRegistrationsId(beacon_id); + [](const Beacon& beacon) { + return SourceRegistrationsId(beacon.id); }, }, data_); @@ -795,13 +801,18 @@ void AttributionDataHostManagerImpl::NotifyFencedFrameReportingBeaconStarted( BeaconId beacon_id, + absl::optional<int64_t> navigation_id, SuitableOrigin source_origin, bool is_within_fenced_frame, AttributionInputEvent input_event, GlobalRenderFrameHostId render_frame_id) { - auto [it, inserted] = registrations_.emplace( - std::move(source_origin), is_within_fenced_frame, std::move(input_event), - render_frame_id, beacon_id); + auto [it, inserted] = + registrations_.emplace(std::move(source_origin), is_within_fenced_frame, + std::move(input_event), render_frame_id, + SourceRegistrations::Beacon{ + .id = beacon_id, + .navigation_id = navigation_id, + }); DCHECK(inserted); // Treat ongoing beacon registrations as a data host for the purpose of @@ -819,8 +830,10 @@ bool is_final_response) { auto it = registrations_.find(beacon_id); - // The registration may no longer be tracked in the event the navigation - // failed. + if (base::FeatureList::IsEnabled(kAttributionFencedFrameReportingBeacon)) { + DCHECK(it != registrations_.end()); + } + if (it == registrations_.end()) { return; } @@ -855,12 +868,7 @@ SourceRegistrationsId id, base::FunctionRef<void(const SourceRegistrations&)> handle_result) { auto it = registrations_.find(id); - - // The registration may no longer be tracked in the event the navigation - // failed. - if (it == registrations_.end()) { - return; - } + DCHECK(it != registrations_.end()); it->DecrementPendingSourceData(); handle_result(*it); @@ -874,8 +882,9 @@ data_decoder::DataDecoder::ValueOrError result) { OnSourceParsed(id, [&](const SourceRegistrations& registrations) { auto source_type = SourceType::kNavigation; - if (const auto* beacon_id = absl::get_if<BeaconId>(®istrations.data()); - beacon_id && absl::holds_alternative<EventBeaconId>(*beacon_id)) { + if (const auto* beacon = + absl::get_if<SourceRegistrations::Beacon>(®istrations.data()); + beacon && !beacon->navigation_id.has_value()) { source_type = SourceType::kEvent; }
diff --git a/content/browser/attribution_reporting/attribution_data_host_manager_impl.h b/content/browser/attribution_reporting/attribution_data_host_manager_impl.h index 2b22f40..c9b577a3 100644 --- a/content/browser/attribution_reporting/attribution_data_host_manager_impl.h +++ b/content/browser/attribution_reporting/attribution_data_host_manager_impl.h
@@ -104,6 +104,7 @@ const blink::AttributionSrcToken& attribution_src_token) override; void NotifyFencedFrameReportingBeaconStarted( BeaconId beacon_id, + absl::optional<int64_t> navigation_id, attribution_reporting::SuitableOrigin source_origin, bool is_within_fenced_frame, AttributionInputEvent input_event,
diff --git a/content/browser/attribution_reporting/attribution_data_host_manager_impl_unittest.cc b/content/browser/attribution_reporting/attribution_data_host_manager_impl_unittest.cc index f39ac41..c316919 100644 --- a/content/browser/attribution_reporting/attribution_data_host_manager_impl_unittest.cc +++ b/content/browser/attribution_reporting/attribution_data_host_manager_impl_unittest.cc
@@ -100,6 +100,9 @@ const GlobalRenderFrameHostId kFrameId = {0, 1}; +constexpr BeaconId kBeaconId(123); +constexpr int64_t kNavigationId(456); + struct ExpectedTriggerQueueEventCounts { base::HistogramBase::Count skipped_queue = 0; base::HistogramBase::Count dropped = 0; @@ -1667,24 +1670,23 @@ auto reporting_origin = url::Origin::Create(GURL("https://report.test")); auto source_origin = *SuitableOrigin::Deserialize("https://source.test"); - NavigationBeaconId navigation_id(123); data_host_manager_.NotifyFencedFrameReportingBeaconStarted( - navigation_id, std::move(source_origin), /*is_within_fenced_frame=*/false, - AttributionInputEvent(), kFrameId); + kBeaconId, kNavigationId, std::move(source_origin), + /*is_within_fenced_frame=*/false, AttributionInputEvent(), kFrameId); auto headers = base::MakeRefCounted<net::HttpResponseHeaders>(""); headers->SetHeader(kAttributionReportingRegisterSourceHeader, kRegisterSourceJson); data_host_manager_.NotifyFencedFrameReportingBeaconData( - navigation_id, reporting_origin, headers.get(), + kBeaconId, reporting_origin, headers.get(), /*is_final_response=*/false); // Wait for parsing to finish. task_environment_.FastForwardBy(base::TimeDelta()); data_host_manager_.NotifyFencedFrameReportingBeaconData( - navigation_id, std::move(reporting_origin), headers.get(), + kBeaconId, std::move(reporting_origin), headers.get(), /*is_final_response=*/true); data_host_manager_.NotifyNavigationFailure(blink::AttributionSrcToken()); @@ -1700,17 +1702,16 @@ auto reporting_origin = url::Origin::Create(GURL("https://report.test")); auto source_origin = *SuitableOrigin::Deserialize("https://source.test"); - NavigationBeaconId navigation_id(123); data_host_manager_.NotifyFencedFrameReportingBeaconStarted( - navigation_id, std::move(source_origin), /*is_within_fenced_frame=*/false, - AttributionInputEvent(), kFrameId); + kBeaconId, kNavigationId, std::move(source_origin), + /*is_within_fenced_frame=*/false, AttributionInputEvent(), kFrameId); auto headers = base::MakeRefCounted<net::HttpResponseHeaders>(""); headers->SetHeader(kAttributionReportingRegisterSourceHeader, kRegisterSourceJson); data_host_manager_.NotifyFencedFrameReportingBeaconData( - navigation_id, reporting_origin, headers.get(), + kBeaconId, reporting_origin, headers.get(), /*is_final_response=*/false); // Wait for parsing to finish. @@ -1719,7 +1720,7 @@ data_host_manager_.NotifyNavigationFailure(blink::AttributionSrcToken()); data_host_manager_.NotifyFencedFrameReportingBeaconData( - navigation_id, std::move(reporting_origin), headers.get(), + kBeaconId, std::move(reporting_origin), headers.get(), /*is_final_response=*/true); // Wait for parsing to finish. @@ -1737,9 +1738,8 @@ auto reporting_origin = url::Origin::Create(GURL("https://report.test")); auto source_origin = *SuitableOrigin::Deserialize("https://source.test"); - NavigationBeaconId navigation_id(123); data_host_manager_.NotifyFencedFrameReportingBeaconStarted( - navigation_id, source_origin, /*is_within_fenced_frame=*/false, + kBeaconId, kNavigationId, source_origin, /*is_within_fenced_frame=*/false, AttributionInputEvent(), kFrameId); auto headers = base::MakeRefCounted<net::HttpResponseHeaders>(""); @@ -1747,14 +1747,14 @@ kRegisterSourceJson); data_host_manager_.NotifyFencedFrameReportingBeaconData( - navigation_id, reporting_origin, headers.get(), + kBeaconId, reporting_origin, headers.get(), /*is_final_response=*/false); // Wait for parsing to finish. task_environment_.FastForwardBy(base::TimeDelta()); data_host_manager_.NotifyFencedFrameReportingBeaconData( - navigation_id, std::move(reporting_origin), headers.get(), + kBeaconId, std::move(reporting_origin), headers.get(), /*is_final_response=*/true); // This is irrelevant to beacon source registrations. @@ -1788,9 +1788,8 @@ kFrameId)) .Times(2); - NavigationBeaconId navigation_id(123); data_host_manager_.NotifyFencedFrameReportingBeaconStarted( - navigation_id, source_origin, /*is_within_fenced_frame=*/false, + kBeaconId, kNavigationId, source_origin, /*is_within_fenced_frame=*/false, AttributionInputEvent(), kFrameId); auto headers = base::MakeRefCounted<net::HttpResponseHeaders>(""); @@ -1798,14 +1797,14 @@ R"("https://r.test/x")"); data_host_manager_.NotifyFencedFrameReportingBeaconData( - navigation_id, reporting_origin, headers.get(), + kBeaconId, reporting_origin, headers.get(), /*is_final_response=*/false); // Wait for parsing to finish. task_environment_.FastForwardBy(base::TimeDelta()); data_host_manager_.NotifyFencedFrameReportingBeaconData( - navigation_id, std::move(reporting_origin), headers.get(), + kBeaconId, std::move(reporting_origin), headers.get(), /*is_final_response=*/true); // This is irrelevant to beacon source registrations. @@ -1833,9 +1832,8 @@ SourceRegistrationError::kInvalidJson)) .Times(2); - NavigationBeaconId navigation_id(123); data_host_manager_.NotifyFencedFrameReportingBeaconStarted( - navigation_id, source_origin, /*is_within_fenced_frame=*/false, + kBeaconId, kNavigationId, source_origin, /*is_within_fenced_frame=*/false, AttributionInputEvent(), kFrameId); auto headers = base::MakeRefCounted<net::HttpResponseHeaders>(""); @@ -1843,14 +1841,14 @@ "!!!invalid json"); data_host_manager_.NotifyFencedFrameReportingBeaconData( - navigation_id, reporting_origin, headers.get(), + kBeaconId, reporting_origin, headers.get(), /*is_final_response=*/false); // Wait for parsing to finish. task_environment_.FastForwardBy(base::TimeDelta()); data_host_manager_.NotifyFencedFrameReportingBeaconData( - navigation_id, reporting_origin, headers.get(), + kBeaconId, reporting_origin, headers.get(), /*is_final_response=*/true); // This is irrelevant to beacon source registrations. @@ -1872,9 +1870,8 @@ auto reporting_origin = url::Origin::Create(GURL("https://report.test")); auto source_origin = *SuitableOrigin::Deserialize("https://source.test"); - NavigationBeaconId navigation_id(123); data_host_manager_.NotifyFencedFrameReportingBeaconStarted( - navigation_id, source_origin, /*is_within_fenced_frame=*/false, + kBeaconId, kNavigationId, source_origin, /*is_within_fenced_frame=*/false, AttributionInputEvent(), kFrameId); auto headers = base::MakeRefCounted<net::HttpResponseHeaders>(""); @@ -1882,7 +1879,7 @@ kRegisterSourceJson); data_host_manager_.NotifyFencedFrameReportingBeaconData( - navigation_id, reporting_origin, headers.get(), + kBeaconId, reporting_origin, headers.get(), /*is_final_response=*/false); // Wait for parsing to finish. @@ -1895,7 +1892,7 @@ /*is_within_fenced_frame=*/false, kFrameId); data_host_manager_.NotifyFencedFrameReportingBeaconData( - navigation_id, std::move(reporting_origin), headers.get(), + kBeaconId, std::move(reporting_origin), headers.get(), /*is_final_response=*/true); // Wait for parsing to finish. @@ -1908,9 +1905,8 @@ auto source_origin = *SuitableOrigin::Deserialize("https://source.test"); - NavigationBeaconId navigation_id(123); data_host_manager_.NotifyFencedFrameReportingBeaconStarted( - navigation_id, source_origin, + kBeaconId, kNavigationId, source_origin, /*is_within_fenced_frame=*/false, AttributionInputEvent(), kFrameId); auto headers = base::MakeRefCounted<net::HttpResponseHeaders>(""); @@ -1918,7 +1914,7 @@ kRegisterSourceJson); data_host_manager_.NotifyFencedFrameReportingBeaconData( - navigation_id, + kBeaconId, /*reporting_origin=*/url::Origin::Create(GURL("http://insecure.test")), headers.get(), /*is_final_response=*/true); @@ -1946,9 +1942,8 @@ EXPECT_CALL(mock_manager_, HandleTrigger); } - NavigationBeaconId navigation_id(123); data_host_manager_.NotifyFencedFrameReportingBeaconStarted( - navigation_id, + kBeaconId, kNavigationId, /*source_origin=*/*SuitableOrigin::Deserialize("https://report.test"), /*is_within_fenced_frame=*/false, AttributionInputEvent(), kFrameId); @@ -1997,21 +1992,20 @@ auto reporting_origin = url::Origin::Create(GURL("https://report.test")); auto source_origin = *SuitableOrigin::Deserialize("https://source.test"); - NavigationBeaconId navigation_id(123); data_host_manager_.NotifyFencedFrameReportingBeaconStarted( - navigation_id, std::move(source_origin), /*is_within_fenced_frame=*/false, - AttributionInputEvent(), kFrameId); + kBeaconId, kNavigationId, std::move(source_origin), + /*is_within_fenced_frame=*/false, AttributionInputEvent(), kFrameId); auto headers = base::MakeRefCounted<net::HttpResponseHeaders>(""); headers->SetHeader(kAttributionReportingRegisterSourceHeader, kRegisterSourceJson); data_host_manager_.NotifyFencedFrameReportingBeaconData( - navigation_id, reporting_origin, headers.get(), + kBeaconId, reporting_origin, headers.get(), /*is_final_response=*/false); data_host_manager_.NotifyFencedFrameReportingBeaconData( - navigation_id, reporting_origin, headers.get(), + kBeaconId, reporting_origin, headers.get(), /*is_final_response=*/true); // Wait for parsing. @@ -2043,13 +2037,12 @@ auto reporting_origin = url::Origin::Create(GURL("https://report.test")); auto source_origin = *SuitableOrigin::Deserialize("https://source.test"); - NavigationBeaconId navigation_id(123); data_host_manager_.NotifyFencedFrameReportingBeaconStarted( - navigation_id, std::move(source_origin), /*is_within_fenced_frame=*/false, - AttributionInputEvent(), kFrameId); + kBeaconId, kNavigationId, std::move(source_origin), + /*is_within_fenced_frame=*/false, AttributionInputEvent(), kFrameId); data_host_manager_.NotifyFencedFrameReportingBeaconData( - navigation_id, /*reporting_origin=*/url::Origin(), /*headers=*/nullptr, + kBeaconId, /*reporting_origin=*/url::Origin(), /*headers=*/nullptr, /*is_final_response=*/true); mojo::Remote<blink::mojom::AttributionDataHost> trigger_data_host_remote; @@ -2073,9 +2066,8 @@ SourceIsWithinFencedFrameIs(true)), kFrameId)); - EventBeaconId event_id(123); data_host_manager_.NotifyFencedFrameReportingBeaconStarted( - event_id, + kBeaconId, /*navigation_id=*/absl::nullopt, /*source_origin=*/*SuitableOrigin::Deserialize("https://source.test"), /*is_within_fenced_frame=*/true, /*input_event=*/AttributionInputEvent(), kFrameId); @@ -2085,7 +2077,7 @@ kRegisterSourceJson); data_host_manager_.NotifyFencedFrameReportingBeaconData( - event_id, + kBeaconId, /*reporting_origin=*/url::Origin::Create(GURL("https://report.test")), headers.get(), /*is_final_response=*/true);
diff --git a/content/browser/attribution_reporting/attribution_host.cc b/content/browser/attribution_reporting/attribution_host.cc index c406e21..9c0d16cd 100644 --- a/content/browser/attribution_reporting/attribution_host.cc +++ b/content/browser/attribution_reporting/attribution_host.cc
@@ -4,6 +4,8 @@ #include "content/browser/attribution_reporting/attribution_host.h" +#include <stdint.h> + #include <utility> #include "base/check.h" @@ -33,6 +35,7 @@ #include "content/public/browser/web_contents.h" #include "content/public/common/content_client.h" #include "mojo/public/cpp/bindings/message.h" +#include "third_party/abseil-cpp/absl/types/optional.h" #include "third_party/abseil-cpp/absl/types/variant.h" #include "third_party/blink/public/common/features.h" #include "third_party/blink/public/common/navigation/impression.h" @@ -418,6 +421,7 @@ void AttributionHost::NotifyFencedFrameReportingBeaconStarted( BeaconId beacon_id, + absl::optional<int64_t> navigation_id, RenderFrameHostImpl* initiator_frame_host) { if (!base::FeatureList::IsEnabled(kAttributionFencedFrameReportingBeacon)) { return; @@ -454,14 +458,14 @@ } AttributionInputEvent input_event; - if (absl::holds_alternative<NavigationBeaconId>(beacon_id)) { + if (navigation_id.has_value()) { input_event = AttributionHost::FromWebContents( WebContents::FromRenderFrameHost(initiator_frame_host)) ->GetMostRecentNavigationInputEvent(); } data_host_manager->NotifyFencedFrameReportingBeaconStarted( - beacon_id, std::move(*initiator_root_frame_origin), + beacon_id, navigation_id, std::move(*initiator_root_frame_origin), initiator_frame_host->IsNestedWithinFencedFrame(), input_event, initiator_root_frame->GetGlobalId()); }
diff --git a/content/browser/attribution_reporting/attribution_host.h b/content/browser/attribution_reporting/attribution_host.h index b4dfb60..ba4e72a 100644 --- a/content/browser/attribution_reporting/attribution_host.h +++ b/content/browser/attribution_reporting/attribution_host.h
@@ -68,8 +68,13 @@ // navigation beacon. // This function should only be invoked if Attribution Reporting API is // enabled on the page. + // `navigation_id` will be set if this beacon is being sent as the result of a + // top navigation initiated by a fenced frame. This is used to track + // attributions that occur on a navigated page after the current page has been + // unloaded. Otherwise `absl::nullopt`. void NotifyFencedFrameReportingBeaconStarted( BeaconId beacon_id, + absl::optional<int64_t> navigation_id, RenderFrameHostImpl* initiator_frame_host); private:
diff --git a/content/browser/attribution_reporting/attribution_host_unittest.cc b/content/browser/attribution_reporting/attribution_host_unittest.cc index bc114848..33eaa38 100644 --- a/content/browser/attribution_reporting/attribution_host_unittest.cc +++ b/content/browser/attribution_reporting/attribution_host_unittest.cc
@@ -4,6 +4,8 @@ #include "content/browser/attribution_reporting/attribution_host.h" +#include <stdint.h> + #include <memory> #include <vector> @@ -34,6 +36,7 @@ #include "mojo/public/cpp/test_support/test_utils.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/abseil-cpp/absl/types/optional.h" #include "third_party/blink/public/common/features.h" #include "third_party/blink/public/common/permissions_policy/origin_with_possible_wildcards.h" #include "third_party/blink/public/common/permissions_policy/permissions_policy_declaration.h" @@ -63,13 +66,15 @@ using ::testing::_; using ::testing::Optional; using ::testing::Return; -using ::testing::VariantWith; using ::attribution_reporting::mojom::RegistrationType; using ::blink::mojom::AttributionNavigationType; const char kConversionUrl[] = "https://b.com"; +constexpr BeaconId kBeaconId(123); +constexpr int64_t kNavigationId(456); + class AttributionHostTest : public RenderViewHostTestHarness { public: AttributionHostTest() = default; @@ -515,7 +520,8 @@ GURL("https://fencedframe.example"), fenced_frame); conversion_host()->NotifyFencedFrameReportingBeaconStarted( - NavigationBeaconId(123), static_cast<RenderFrameHostImpl*>(fenced_frame)); + kBeaconId, kNavigationId, + static_cast<RenderFrameHostImpl*>(fenced_frame)); } TEST_F(AttributionHostTest, NotifyFencedFrameReportingBeaconStarted) { @@ -533,15 +539,13 @@ {"https:/secure.com", true}, }; - NavigationBeaconId navigation_id(123); - for (const auto& test_case : kTestCases) { contents()->NavigateAndCommit(GURL(test_case.source_origin)); if (test_case.expected_valid) { EXPECT_CALL( *mock_data_host_manager(), NotifyFencedFrameReportingBeaconStarted( - VariantWith<NavigationBeaconId>(navigation_id), + kBeaconId, Optional(kNavigationId), *SuitableOrigin::Deserialize(test_case.source_origin), /*is_within_fenced_frame=*/true, _, main_rfh()->GetGlobalId())); } else { @@ -559,7 +563,8 @@ GURL("https://fencedframe.example"), fenced_frame); conversion_host()->NotifyFencedFrameReportingBeaconStarted( - navigation_id, static_cast<RenderFrameHostImpl*>(fenced_frame)); + kBeaconId, kNavigationId, + static_cast<RenderFrameHostImpl*>(fenced_frame)); } } @@ -606,7 +611,8 @@ fenced_frame = simulator->GetFinalRenderFrameHost(); conversion_host()->NotifyFencedFrameReportingBeaconStarted( - EventBeaconId(123), static_cast<RenderFrameHostImpl*>(fenced_frame)); + kBeaconId, /*navigation_id=*/absl::nullopt, + static_cast<RenderFrameHostImpl*>(fenced_frame)); } }
diff --git a/content/browser/attribution_reporting/test/mock_attribution_data_host_manager.h b/content/browser/attribution_reporting/test/mock_attribution_data_host_manager.h index db3ee55..64bdc09a 100644 --- a/content/browser/attribution_reporting/test/mock_attribution_data_host_manager.h +++ b/content/browser/attribution_reporting/test/mock_attribution_data_host_manager.h
@@ -5,6 +5,8 @@ #ifndef CONTENT_BROWSER_ATTRIBUTION_REPORTING_TEST_MOCK_ATTRIBUTION_DATA_HOST_MANAGER_H_ #define CONTENT_BROWSER_ATTRIBUTION_REPORTING_TEST_MOCK_ATTRIBUTION_DATA_HOST_MANAGER_H_ +#include <stdint.h> + #include "components/attribution_reporting/registration_type.mojom-forward.h" #include "components/attribution_reporting/suitable_origin.h" #include "content/browser/attribution_reporting/attribution_beacon_id.h" @@ -13,6 +15,7 @@ #include "content/public/browser/global_routing_id.h" #include "mojo/public/cpp/bindings/pending_receiver.h" #include "testing/gmock/include/gmock/gmock.h" +#include "third_party/abseil-cpp/absl/types/optional.h" #include "third_party/blink/public/common/tokens/tokens.h" #include "third_party/blink/public/mojom/conversions/attribution_data_host.mojom-forward.h" #include "third_party/blink/public/mojom/conversions/attribution_reporting.mojom-forward.h" @@ -75,6 +78,7 @@ MOCK_METHOD(void, NotifyFencedFrameReportingBeaconStarted, (BeaconId beacon_id, + absl::optional<int64_t> navigation_id, attribution_reporting::SuitableOrigin source_origin, bool is_within_fenced_frame, AttributionInputEvent input_event,
diff --git a/content/browser/fenced_frame/fenced_frame_browsertest.cc b/content/browser/fenced_frame/fenced_frame_browsertest.cc index ebbd36a..3becac58 100644 --- a/content/browser/fenced_frame/fenced_frame_browsertest.cc +++ b/content/browser/fenced_frame/fenced_frame_browsertest.cc
@@ -16,6 +16,7 @@ #include "base/time/time.h" #include "components/network_session_configurator/common/network_switches.h" #include "components/ukm/test_ukm_recorder.h" +#include "content/browser/attribution_reporting/attribution_features.h" #include "content/browser/attribution_reporting/attribution_manager.h" #include "content/browser/back_forward_cache_browsertest.h" #include "content/browser/fenced_frame/fenced_frame.h" @@ -4549,7 +4550,8 @@ // supporting iframes. FencedFrameReportEventBrowserTest() { scoped_feature_list_.InitWithFeaturesAndParameters( - {{blink::features::kAllowURNsInIframes, {}}}, + {{blink::features::kAllowURNsInIframes, {}}, + {kAttributionFencedFrameReportingBeacon, {}}}, {/* disabled_features */}); } @@ -6027,8 +6029,10 @@ public testing::WithParamInterface<const char*> { public: FencedFrameAutomaticBeaconBrowserTest() { - scoped_feature_list_.InitAndEnableFeature( - blink::features::kAllowURNsInIframes); + scoped_feature_list_.InitWithFeatures( + /*enabled_features=*/{blink::features::kAllowURNsInIframes, + kAttributionFencedFrameReportingBeacon}, + /*disabled_features=*/{}); } // An object representing the configuration of the test. First a frame is
diff --git a/content/browser/fenced_frame/fenced_frame_reporter.cc b/content/browser/fenced_frame/fenced_frame_reporter.cc index e0534d6..2d5aa16 100644 --- a/content/browser/fenced_frame/fenced_frame_reporter.cc +++ b/content/browser/fenced_frame/fenced_frame_reporter.cc
@@ -136,11 +136,13 @@ const std::string& type, const std::string& data, const url::Origin& request_initiator, - BeaconId beacon_id) + BeaconId beacon_id, + bool is_automatic_beacon) : type(type), data(data), request_initiator(request_initiator), - beacon_id(beacon_id) {} + beacon_id(beacon_id), + is_automatic_beacon(is_automatic_beacon) {} FencedFrameReporter::PendingEvent::PendingEvent(const PendingEvent&) = default; @@ -248,7 +250,9 @@ std::string ignored_error_message; SendReportInternal(it->second, pending_event.type, pending_event.data, reporting_destination, pending_event.request_initiator, - pending_event.beacon_id, ignored_error_message); + pending_event.beacon_id, + pending_event.is_automatic_beacon, + ignored_error_message); } } @@ -291,37 +295,31 @@ static base::AtomicSequenceNumber unique_id_counter; - // The id will be a `NavigationBeaconId` only if this report is being sent as - // the result of a top navigation initiated by a fenced frame. This is used to - // track attributions that occur on a navigated page after the current page - // has been unloaded. - BeaconId beacon_id; - if (navigation_id.has_value()) { - beacon_id = NavigationBeaconId(navigation_id.value()); - } else { - beacon_id = EventBeaconId(unique_id_counter.GetNext()); - } + BeaconId beacon_id(unique_id_counter.GetNext()); auto* attribution_host = AttributionHost::FromWebContents( WebContents::FromRenderFrameHost(request_initiator_frame)); if (attribution_host) { attribution_host->NotifyFencedFrameReportingBeaconStarted( - beacon_id, request_initiator_frame); + beacon_id, navigation_id, request_initiator_frame); } const url::Origin& request_initiator = request_initiator_frame->GetLastCommittedOrigin(); + const bool is_automatic_beacon = navigation_id.has_value(); + // If the reporting URL map is pending, queue the event. if (it->second.reporting_url_map == absl::nullopt) { it->second.pending_events.emplace_back(event_type, event_data, - request_initiator, beacon_id); + request_initiator, beacon_id, + is_automatic_beacon); return true; } return SendReportInternal(it->second, event_type, event_data, reporting_destination, request_initiator, beacon_id, - error_message); + is_automatic_beacon, error_message); } bool FencedFrameReporter::SendReportInternal( @@ -331,6 +329,7 @@ blink::FencedFrame::ReportingDestination reporting_destination, const url::Origin& request_initiator, BeaconId beacon_id, + bool is_automatic_beacon, std::string& error_message) { // The URL map should not be pending at this point. DCHECK(reporting_destination_info.reporting_url_map); @@ -371,10 +370,9 @@ net::IsolationInfo::CreateTransient(); if (attribution_manager_) { - request->headers.SetHeader("Attribution-Reporting-Eligible", - absl::holds_alternative<EventBeaconId>(beacon_id) - ? "event-source" - : "navigation-source"); + request->headers.SetHeader( + "Attribution-Reporting-Eligible", + is_automatic_beacon ? "navigation-source" : "event-source"); if (base::FeatureList::IsEnabled( blink::features::kAttributionReportingCrossAppWeb)) {
diff --git a/content/browser/fenced_frame/fenced_frame_reporter.h b/content/browser/fenced_frame/fenced_frame_reporter.h index 2fe8b94..d28269a 100644 --- a/content/browser/fenced_frame/fenced_frame_reporter.h +++ b/content/browser/fenced_frame/fenced_frame_reporter.h
@@ -194,7 +194,8 @@ PendingEvent(const std::string& type, const std::string& data, const url::Origin& request_initiator, - BeaconId beacon_id); + BeaconId beacon_id, + bool is_automatic_beacon); PendingEvent(const PendingEvent&); PendingEvent(PendingEvent&&); @@ -208,6 +209,7 @@ std::string data; url::Origin request_initiator; BeaconId beacon_id; + bool is_automatic_beacon; }; // The per-blink::FencedFrame::ReportingDestination reporting information. @@ -240,6 +242,7 @@ blink::FencedFrame::ReportingDestination reporting_destination, const url::Origin& request_initiator, BeaconId beacon_id, + bool is_automatic_beacon, std::string& error_message); // Helper to send private aggregation requests in
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn index 33d73d5..07e327ec 100644 --- a/content/test/BUILD.gn +++ b/content/test/BUILD.gn
@@ -2949,7 +2949,10 @@ } if (is_fuchsia) { - deps += [ "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.accessibility.semantics:fuchsia.accessibility.semantics_hlcpp" ] + deps += [ + "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.accessibility.semantics:fuchsia.accessibility.semantics_cpp", + "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.accessibility.semantics:fuchsia.accessibility.semantics_hlcpp", + ] } if (enable_printing) {
diff --git a/extensions/browser/api/runtime/runtime_api.cc b/extensions/browser/api/runtime/runtime_api.cc index d112f7e..f299478 100644 --- a/extensions/browser/api/runtime/runtime_api.cc +++ b/extensions/browser/api/runtime/runtime_api.cc
@@ -153,6 +153,51 @@ return url_string; } +// Returns true if the given `context` matches the `filter`. +bool ExtensionContextMatchesFilter( + const api::runtime::ExtensionContext& context, + const api::runtime::ContextFilter& filter) { + if (filter.context_types && + !base::Contains(*filter.context_types, context.context_type)) { + return false; + } + if (filter.context_ids && + !base::Contains(*filter.context_ids, context.context_id)) { + return false; + } + if (filter.tab_ids && !base::Contains(*filter.tab_ids, context.tab_id)) { + return false; + } + if (filter.window_ids && + !base::Contains(*filter.window_ids, context.window_id)) { + return false; + } + if (filter.document_ids && + (!context.document_id || + !base::Contains(*filter.document_ids, *context.document_id))) { + return false; + } + if (filter.frame_ids && + !base::Contains(*filter.frame_ids, context.frame_id)) { + return false; + } + if (filter.document_urls && + (!context.document_url || + !base::Contains(*filter.document_urls, *context.document_url))) { + return false; + } + if (filter.document_origins && + (!context.document_origin || + !base::Contains(*filter.document_origins, *context.document_origin))) { + return false; + } + if (filter.incognito && *filter.incognito != context.incognito) { + return false; + } + + return true; +} + } // namespace /////////////////////////////////////////////////////////////////////////////// @@ -751,12 +796,29 @@ ExtensionFunction::ResponseAction RuntimeGetContextsFunction::Run() { EXTENSION_FUNCTION_VALIDATE(extension()); + auto params = api::runtime::GetContexts::Params::Create(args()); + EXTENSION_FUNCTION_VALIDATE(params); + const api::runtime::ContextFilter& filter = params->filter; + std::vector<api::runtime::ExtensionContext> result; - if (absl::optional<api::runtime::ExtensionContext> worker = - GetWorkerContext()) { - result.push_back(std::move(*worker)); + + // Minor optimization: only construct the context if there's a chance it will + // match the filter. + if (!filter.context_types || + base::Contains(*filter.context_types, + api::runtime::CONTEXT_TYPE_BACKGROUND)) { + if (absl::optional<api::runtime::ExtensionContext> worker = + GetWorkerContext()) { + result.push_back(std::move(*worker)); + } } + // Erase any contexts that don't match the specified filter. + base::EraseIf(result, + [&filter](const api::runtime::ExtensionContext& context) { + return !ExtensionContextMatchesFilter(context, filter); + }); + return RespondNow( ArgumentList(api::runtime::GetContexts::Results::Create(result))); }
diff --git a/extensions/common/api/runtime.json b/extensions/common/api/runtime.json index 13f0b63..e3b350d 100644 --- a/extensions/common/api/runtime.json +++ b/extensions/common/api/runtime.json
@@ -188,6 +188,57 @@ "description": "Whether the context is associated with an incognito profile." } } + }, + { + "id": "ContextFilter", + "type": "object", + "description": "A filter to match against certain extension contexts. Matching contexts must match all specified filters; any filter that is not specified matches all available contexts. Thus, a filter of `{}` will match all available contexts.", + "properties": { + "contextTypes": { + "type": "array", + "items": { "$ref": "ContextType" }, + "optional": true + }, + "contextIds": { + "type": "array", + "items": { "type": "string" }, + "optional": true + }, + "tabIds": { + "type": "array", + "items": { "type": "integer" }, + "optional": true + }, + "windowIds": { + "type": "array", + "items": { "type": "integer" }, + "optional": true + }, + "documentIds": { + "type": "array", + "items": { "type": "string" }, + "optional": true + }, + "frameIds": { + "type": "array", + "items": { "type": "integer" }, + "optional": true + }, + "documentUrls": { + "type": "array", + "items": { "type": "string" }, + "optional": true + }, + "documentOrigins": { + "type": "array", + "items": { "type": "string" }, + "optional": true + }, + "incognito": { + "type": "boolean", + "optional": true + } + } } ], "properties": { @@ -491,7 +542,11 @@ "type": "function", "description": "Fetches information about active contexts associated with this extension", "parameters": [ - // TODO(crbug/1426192): Add a filter. + { + "$ref": "ContextFilter", + "name": "filter", + "description": "A filter to find matching contexts. A context matches if it matches all specified fields in the filter. Any unspecified field in the filter matches all contexts." + } ], "returns_async": { "name": "callback",
diff --git a/gpu/BUILD.gn b/gpu/BUILD.gn index 671a9e7..8cd9016 100644 --- a/gpu/BUILD.gn +++ b/gpu/BUILD.gn
@@ -305,6 +305,8 @@ "command_buffer/service/shared_image/shared_image_factory_unittest.cc", "command_buffer/service/shared_image/shared_image_manager_unittest.cc", "command_buffer/service/shared_image/shared_image_representation_unittest.cc", + "command_buffer/service/shared_image/shared_image_test_base.cc", + "command_buffer/service/shared_image/shared_image_test_base.h", "command_buffer/service/shared_image/test_utils.cc", "command_buffer/service/shared_image/test_utils.h", "command_buffer/service/shared_image/wrapped_sk_image_backing_factory_unittest.cc", @@ -399,6 +401,7 @@ "//gpu/ipc:gl_in_process_context", "//gpu/ipc/host", "//gpu/ipc/service", + "//gpu/vulkan:buildflags", "//gpu/webgpu:common", "//mojo/core/embedder", "//testing/gmock", @@ -417,6 +420,10 @@ deps += [ "//ui/ozone" ] } + if (enable_vulkan) { + deps += [ "//gpu/vulkan/init:init" ] + } + libs = [] if (is_android) { @@ -440,7 +447,6 @@ # Simply loading the Vulkan driver leaks crbug.com/1134681 # CFI error in third_party/vulkan_memory_allocator crbug.com/1139916 if (enable_vulkan && !is_lsan && !is_cfi) { - deps += [ "//gpu/vulkan/init:init" ] sources += [ "command_buffer/service/shared_image/external_vk_image_backing_factory_unittest.cc" ] } }
diff --git a/gpu/command_buffer/service/shared_image/DEPS b/gpu/command_buffer/service/shared_image/DEPS index 3dab9e7e..3220bd1 100644 --- a/gpu/command_buffer/service/shared_image/DEPS +++ b/gpu/command_buffer/service/shared_image/DEPS
@@ -3,11 +3,14 @@ ] specific_include_rules = { + "dcomp_image_backing_factory_unittest\.cc": [ + "+ui/platform_window", + ], "external_vk_image_backing_factory_unittest\.cc": [ "+components/viz/common/gpu/vulkan_in_process_context_provider.h", ], - "dcomp_image_backing_factory_unittest\.cc": [ - "+ui/platform_window", + "shared_image_test_base\.cc": [ + "+components/viz/common/gpu/vulkan_in_process_context_provider.h", ], ".*_unittest\.cc": [ "+cc/test",
diff --git a/gpu/command_buffer/service/shared_image/ahardwarebuffer_image_backing_factory_unittest.cc b/gpu/command_buffer/service/shared_image/ahardwarebuffer_image_backing_factory_unittest.cc index 90fb10c4..8644ade 100644 --- a/gpu/command_buffer/service/shared_image/ahardwarebuffer_image_backing_factory_unittest.cc +++ b/gpu/command_buffer/service/shared_image/ahardwarebuffer_image_backing_factory_unittest.cc
@@ -15,9 +15,9 @@ #include "gpu/command_buffer/service/shared_image/shared_image_factory.h" #include "gpu/command_buffer/service/shared_image/shared_image_manager.h" #include "gpu/command_buffer/service/shared_image/shared_image_representation.h" +#include "gpu/command_buffer/service/shared_image/shared_image_test_base.h" #include "gpu/command_buffer/service/shared_image/test_utils.h" #include "gpu/command_buffer/service/texture_manager.h" -#include "gpu/config/gpu_driver_bug_workarounds.h" #include "gpu/config/gpu_feature_info.h" #include "gpu/config/gpu_preferences.h" #include "testing/gtest/include/gtest/gtest.h" @@ -29,69 +29,31 @@ #include "third_party/skia/include/gpu/GrDirectContext.h" #include "ui/gfx/color_space.h" #include "ui/gl/gl_bindings.h" -#include "ui/gl/gl_context.h" #include "ui/gl/gl_gl_api_implementation.h" -#include "ui/gl/gl_surface.h" -#include "ui/gl/gl_utils.h" -#include "ui/gl/init/gl_factory.h" namespace gpu { namespace { -class AHardwareBufferImageBackingFactoryTest : public testing::Test { +class AHardwareBufferImageBackingFactoryTest : public SharedImageTestBase { public: void SetUp() override { // AHardwareBuffer is only supported on ANDROID O+. Hence these tests // should not be run on android versions less that O. - if (!base::AndroidHardwareBufferCompat::IsSupportAvailable()) - return; - surface_ = gl::init::CreateOffscreenGLSurface(gl::GetDefaultDisplayEGL(), - gfx::Size()); - ASSERT_TRUE(surface_); - context_ = gl::init::CreateGLContext(nullptr, surface_.get(), - gl::GLContextAttribs()); - ASSERT_TRUE(context_); - bool result = context_->MakeCurrent(surface_.get()); - ASSERT_TRUE(result); + if (!base::AndroidHardwareBufferCompat::IsSupportAvailable()) { + GTEST_SKIP() << "AHardwareBuffer not supported"; + } - GpuDriverBugWorkarounds workarounds; - auto gpu_preferences = GpuPreferences(); - - scoped_refptr<gl::GLShareGroup> share_group = new gl::GLShareGroup(); - context_state_ = base::MakeRefCounted<SharedContextState>( - std::move(share_group), surface_, context_, - /*use_virtualized_gl_contexts=*/false, base::DoNothing()); - context_state_->InitializeGrContext(gpu_preferences, workarounds, nullptr); - auto feature_info = - base::MakeRefCounted<gles2::FeatureInfo>(workarounds, GpuFeatureInfo()); - context_state_->InitializeGL(gpu_preferences, std::move(feature_info)); + ASSERT_NO_FATAL_FAILURE(InitializeContext(GrContextType::kGL)); backing_factory_ = std::make_unique<AHardwareBufferImageBackingFactory>( - context_state_->feature_info(), gpu_preferences); - - memory_type_tracker_ = std::make_unique<MemoryTypeTracker>(nullptr); - shared_image_representation_factory_ = - std::make_unique<SharedImageRepresentationFactory>( - &shared_image_manager_, nullptr); + context_state_->feature_info(), gpu_preferences_); } - - GrDirectContext* gr_context() { return context_state_->gr_context(); } - - protected: - scoped_refptr<gl::GLSurface> surface_; - scoped_refptr<gl::GLContext> context_; - scoped_refptr<SharedContextState> context_state_; - std::unique_ptr<AHardwareBufferImageBackingFactory> backing_factory_; - SharedImageManager shared_image_manager_; - std::unique_ptr<MemoryTypeTracker> memory_type_tracker_; - std::unique_ptr<SharedImageRepresentationFactory> - shared_image_representation_factory_; }; class GlLegacySharedImage { public: GlLegacySharedImage( - AHardwareBufferImageBackingFactory* backing_factory, + SharedImageBackingFactory* backing_factory, bool is_thread_safe, SharedImageManager* shared_image_manager, MemoryTypeTracker* memory_type_tracker, @@ -110,15 +72,12 @@ // Basic test to check creation and deletion of AHB backed shared image. TEST_F(AHardwareBufferImageBackingFactoryTest, Basic) { - if (!base::AndroidHardwareBufferCompat::IsSupportAvailable()) - return; - GlLegacySharedImage gl_legacy_shared_image{ backing_factory_.get(), /*is_thread_safe=*/false, &shared_image_manager_, - memory_type_tracker_.get(), shared_image_representation_factory_.get()}; + &memory_type_tracker_, &shared_image_representation_factory_}; // Validate a SkiaImageRepresentation. - auto skia_representation = shared_image_representation_factory_->ProduceSkia( + auto skia_representation = shared_image_representation_factory_.ProduceSkia( gl_legacy_shared_image.mailbox(), context_state_.get()); EXPECT_TRUE(skia_representation); std::vector<GrBackendSemaphore> begin_semaphores; @@ -157,9 +116,6 @@ // We write to a GL texture using gl representation and then read from skia // representation. TEST_F(AHardwareBufferImageBackingFactoryTest, GLSkiaGL) { - if (!base::AndroidHardwareBufferCompat::IsSupportAvailable()) - return; - // Create a backing using mailbox. auto mailbox = Mailbox::GenerateForSharedImage(); auto format = viz::SinglePlaneFormat::kRGBA_8888; @@ -176,12 +132,11 @@ GLenum expected_target = GL_TEXTURE_2D; std::unique_ptr<SharedImageRepresentationFactoryRef> factory_ref = - shared_image_manager_.Register(std::move(backing), - memory_type_tracker_.get()); + shared_image_manager_.Register(std::move(backing), &memory_type_tracker_); // Create a GLTextureImageRepresentation. auto gl_representation = - shared_image_representation_factory_->ProduceGLTexture(mailbox); + shared_image_representation_factory_.ProduceGLTexture(mailbox); EXPECT_TRUE(gl_representation); EXPECT_EQ(expected_target, gl_representation->GetTexture()->target()); @@ -206,7 +161,7 @@ gl_representation.reset(); auto dst_pixels = ReadPixels(mailbox, size, context_state_.get(), - shared_image_representation_factory_.get()); + &shared_image_representation_factory_); // Compare the pixel values. EXPECT_EQ(dst_pixels[0], 0); @@ -218,9 +173,6 @@ } TEST_F(AHardwareBufferImageBackingFactoryTest, InitialData) { - if (!base::AndroidHardwareBufferCompat::IsSupportAvailable()) - return; - auto mailbox = Mailbox::GenerateForSharedImage(); auto format = viz::SinglePlaneFormat::kRGBA_8888; gfx::Size size(4, 4); @@ -241,11 +193,10 @@ EXPECT_TRUE(backing); std::unique_ptr<SharedImageRepresentationFactoryRef> factory_ref = - shared_image_manager_.Register(std::move(backing), - memory_type_tracker_.get()); + shared_image_manager_.Register(std::move(backing), &memory_type_tracker_); auto dst_pixels = ReadPixels(mailbox, size, context_state_.get(), - shared_image_representation_factory_.get()); + &shared_image_representation_factory_); // Compare the pixel values. DCHECK(dst_pixels.size() == initial_data.size()); @@ -256,9 +207,6 @@ // Test to check invalid format support. TEST_F(AHardwareBufferImageBackingFactoryTest, InvalidFormat) { - if (!base::AndroidHardwareBufferCompat::IsSupportAvailable()) - return; - auto mailbox = Mailbox::GenerateForSharedImage(); auto format = viz::SharedImageFormat::SinglePlane( viz::ResourceFormat::YUV_420_BIPLANAR); @@ -276,9 +224,6 @@ // Test to check invalid size support. TEST_F(AHardwareBufferImageBackingFactoryTest, InvalidSize) { - if (!base::AndroidHardwareBufferCompat::IsSupportAvailable()) - return; - auto mailbox = Mailbox::GenerateForSharedImage(); auto format = viz::SinglePlaneFormat::kRGBA_8888; gfx::Size size(0, 0); @@ -300,9 +245,6 @@ } TEST_F(AHardwareBufferImageBackingFactoryTest, EstimatedSize) { - if (!base::AndroidHardwareBufferCompat::IsSupportAvailable()) - return; - auto mailbox = Mailbox::GenerateForSharedImage(); auto format = viz::SinglePlaneFormat::kRGBA_8888; gfx::Size size(256, 256); @@ -320,9 +262,8 @@ EXPECT_GT(backing_estimated_size, 0u); std::unique_ptr<SharedImageRepresentationFactoryRef> shared_image = - shared_image_manager_.Register(std::move(backing), - memory_type_tracker_.get()); - EXPECT_EQ(backing_estimated_size, memory_type_tracker_->GetMemRepresented()); + shared_image_manager_.Register(std::move(backing), &memory_type_tracker_); + EXPECT_EQ(backing_estimated_size, memory_type_tracker_.GetMemRepresented()); shared_image.reset(); } @@ -330,14 +271,11 @@ // TODO(crbug/994720): Failing on Android builders. // Test to check that only one context can write at a time TEST_F(AHardwareBufferImageBackingFactoryTest, DISABLED_OnlyOneWriter) { - if (!base::AndroidHardwareBufferCompat::IsSupportAvailable()) - return; - GlLegacySharedImage gl_legacy_shared_image{ backing_factory_.get(), /*is_thread_safe=*/true, &shared_image_manager_, - memory_type_tracker_.get(), shared_image_representation_factory_.get()}; + &memory_type_tracker_, &shared_image_representation_factory_}; - auto skia_representation = shared_image_representation_factory_->ProduceSkia( + auto skia_representation = shared_image_representation_factory_.ProduceSkia( gl_legacy_shared_image.mailbox(), context_state_.get()); std::vector<GrBackendSemaphore> begin_semaphores; @@ -351,7 +289,7 @@ EXPECT_EQ(0u, begin_semaphores.size()); EXPECT_EQ(0u, end_semaphores.size()); - auto skia_representation2 = shared_image_representation_factory_->ProduceSkia( + auto skia_representation2 = shared_image_representation_factory_.ProduceSkia( gl_legacy_shared_image.mailbox(), context_state_.get()); std::vector<GrBackendSemaphore> begin_semaphores2; std::vector<GrBackendSemaphore> end_semaphores2; @@ -371,16 +309,13 @@ // Test to check that multiple readers are allowed TEST_F(AHardwareBufferImageBackingFactoryTest, CanHaveMultipleReaders) { - if (!base::AndroidHardwareBufferCompat::IsSupportAvailable()) - return; - GlLegacySharedImage gl_legacy_shared_image{ backing_factory_.get(), /*is_thread_safe=*/true, &shared_image_manager_, - memory_type_tracker_.get(), shared_image_representation_factory_.get()}; + &memory_type_tracker_, &shared_image_representation_factory_}; - auto skia_representation = shared_image_representation_factory_->ProduceSkia( + auto skia_representation = shared_image_representation_factory_.ProduceSkia( gl_legacy_shared_image.mailbox(), context_state_.get()); - auto skia_representation2 = shared_image_representation_factory_->ProduceSkia( + auto skia_representation2 = shared_image_representation_factory_.ProduceSkia( gl_legacy_shared_image.mailbox(), context_state_.get()); std::vector<GrBackendSemaphore> begin_semaphores; @@ -408,14 +343,11 @@ // Test to check that a context cannot write while another context is reading TEST_F(AHardwareBufferImageBackingFactoryTest, CannotWriteWhileReading) { - if (!base::AndroidHardwareBufferCompat::IsSupportAvailable()) - return; - GlLegacySharedImage gl_legacy_shared_image{ backing_factory_.get(), /*is_thread_safe=*/true, &shared_image_manager_, - memory_type_tracker_.get(), shared_image_representation_factory_.get()}; + &memory_type_tracker_, &shared_image_representation_factory_}; - auto skia_representation = shared_image_representation_factory_->ProduceSkia( + auto skia_representation = shared_image_representation_factory_.ProduceSkia( gl_legacy_shared_image.mailbox(), context_state_.get()); std::vector<GrBackendSemaphore> begin_semaphores; @@ -428,7 +360,7 @@ EXPECT_EQ(0u, begin_semaphores.size()); EXPECT_EQ(0u, end_semaphores.size()); - auto skia_representation2 = shared_image_representation_factory_->ProduceSkia( + auto skia_representation2 = shared_image_representation_factory_.ProduceSkia( gl_legacy_shared_image.mailbox(), context_state_.get()); std::vector<GrBackendSemaphore> begin_semaphores2; @@ -450,14 +382,11 @@ // Test to check that a context cannot read while another context is writing TEST_F(AHardwareBufferImageBackingFactoryTest, CannotReadWhileWriting) { - if (!base::AndroidHardwareBufferCompat::IsSupportAvailable()) - return; - GlLegacySharedImage gl_legacy_shared_image{ backing_factory_.get(), /*is_thread_safe=*/true, &shared_image_manager_, - memory_type_tracker_.get(), shared_image_representation_factory_.get()}; + &memory_type_tracker_, &shared_image_representation_factory_}; - auto skia_representation = shared_image_representation_factory_->ProduceSkia( + auto skia_representation = shared_image_representation_factory_.ProduceSkia( gl_legacy_shared_image.mailbox(), context_state_.get()); std::vector<GrBackendSemaphore> begin_semaphores; std::vector<GrBackendSemaphore> end_semaphores; @@ -470,7 +399,7 @@ EXPECT_EQ(0u, begin_semaphores.size()); EXPECT_EQ(0u, end_semaphores.size()); - auto skia_representation2 = shared_image_representation_factory_->ProduceSkia( + auto skia_representation2 = shared_image_representation_factory_.ProduceSkia( gl_legacy_shared_image.mailbox(), context_state_.get()); std::vector<GrBackendSemaphore> begin_semaphores2; std::vector<GrBackendSemaphore> end_semaphores2; @@ -487,7 +416,7 @@ } GlLegacySharedImage::GlLegacySharedImage( - AHardwareBufferImageBackingFactory* backing_factory, + SharedImageBackingFactory* backing_factory, bool is_thread_safe, SharedImageManager* shared_image_manager, MemoryTypeTracker* memory_type_tracker, @@ -538,14 +467,11 @@ } TEST_F(AHardwareBufferImageBackingFactoryTest, Overlay) { - if (!base::AndroidHardwareBufferCompat::IsSupportAvailable()) - return; - GlLegacySharedImage gl_legacy_shared_image{ backing_factory_.get(), /*is_thread_safe=*/false, &shared_image_manager_, - memory_type_tracker_.get(), shared_image_representation_factory_.get()}; + &memory_type_tracker_, &shared_image_representation_factory_}; - auto skia_representation = shared_image_representation_factory_->ProduceSkia( + auto skia_representation = shared_image_representation_factory_.ProduceSkia( gl_legacy_shared_image.mailbox(), context_state_.get()); std::vector<GrBackendSemaphore> begin_semaphores; @@ -559,7 +485,7 @@ scoped_write_access.reset(); auto overlay_representation = - shared_image_representation_factory_->ProduceOverlay( + shared_image_representation_factory_.ProduceOverlay( gl_legacy_shared_image.mailbox()); EXPECT_TRUE(overlay_representation);
diff --git a/gpu/command_buffer/service/shared_image/external_vk_image_backing_factory_unittest.cc b/gpu/command_buffer/service/shared_image/external_vk_image_backing_factory_unittest.cc index e2d97d4..a41cbbd 100644 --- a/gpu/command_buffer/service/shared_image/external_vk_image_backing_factory_unittest.cc +++ b/gpu/command_buffer/service/shared_image/external_vk_image_backing_factory_unittest.cc
@@ -19,9 +19,9 @@ #include "gpu/command_buffer/service/shared_image/shared_image_factory.h" #include "gpu/command_buffer/service/shared_image/shared_image_manager.h" #include "gpu/command_buffer/service/shared_image/shared_image_representation.h" +#include "gpu/command_buffer/service/shared_image/shared_image_test_base.h" #include "gpu/command_buffer/service/skia_utils.h" #include "gpu/config/gpu_test_config.h" -#include "gpu/vulkan/init/vulkan_factory.h" #include "gpu/vulkan/vulkan_implementation.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/skia/include/core/SkCanvas.h" @@ -32,10 +32,6 @@ #include "third_party/skia/include/gpu/GrBackendSemaphore.h" #include "third_party/skia/include/gpu/ganesh/SkImageGanesh.h" #include "ui/gl/buildflags.h" -#include "ui/gl/gl_context.h" -#include "ui/gl/gl_surface.h" -#include "ui/gl/gl_utils.h" -#include "ui/gl/init/gl_factory.h" #if BUILDFLAG(USE_DAWN) #include <dawn/dawn_proc.h> @@ -46,77 +42,17 @@ namespace gpu { namespace { -class ExternalVkImageBackingFactoryTest : public testing::Test { +class ExternalVkImageBackingFactoryTest : public SharedImageTestBase { protected: - bool use_passthrough() const { - return gles2::UsePassthroughCommandDecoder( - base::CommandLine::ForCurrentProcess()) && - gles2::PassthroughCommandDecoderSupported(); - } - - GrDirectContext* gr_context() { return context_state_->gr_context(); } - void SetUp() override { #if BUILDFLAG(IS_CHROMEOS) GTEST_SKIP() << "Chrome OS Vulkan initialization fails"; #else - // Set up the Vulkan implementation and context provider. - vulkan_implementation_ = gpu::CreateVulkanImplementation(); - ASSERT_TRUE(vulkan_implementation_); - - auto initialize_vulkan = vulkan_implementation_->InitializeVulkanInstance(); - ASSERT_TRUE(initialize_vulkan); - - vulkan_context_provider_ = viz::VulkanInProcessContextProvider::Create( - vulkan_implementation_.get()); - ASSERT_TRUE(vulkan_context_provider_); - - // Set up a GL context. We don't actually need it, but we can't make - // a SharedContextState without one. - gl_surface_ = gl::init::CreateOffscreenGLSurface(gl::GetDefaultDisplayEGL(), - gfx::Size()); - ASSERT_TRUE(gl_surface_); - gl_context_ = gl::init::CreateGLContext(nullptr, gl_surface_.get(), - gl::GLContextAttribs()); - ASSERT_TRUE(gl_context_); - bool make_current_result = gl_context_->MakeCurrent(gl_surface_.get()); - ASSERT_TRUE(make_current_result); - - scoped_refptr<gl::GLShareGroup> share_group = new gl::GLShareGroup(); - context_state_ = base::MakeRefCounted<SharedContextState>( - std::move(share_group), gl_surface_, gl_context_, - /*use_virtualized_gl_contexts=*/false, base::DoNothing(), - GrContextType::kVulkan, vulkan_context_provider_.get()); - - context_state_->InitializeGL( - GpuPreferences(), base::MakeRefCounted<gles2::FeatureInfo>( - GpuDriverBugWorkarounds(), GpuFeatureInfo())); - - GpuPreferences gpu_preferences = {}; - GpuDriverBugWorkarounds workarounds = {}; - context_state_->InitializeGrContext(gpu_preferences, workarounds, nullptr); - - memory_type_tracker_ = std::make_unique<MemoryTypeTracker>(nullptr); - shared_image_representation_factory_ = - std::make_unique<SharedImageRepresentationFactory>( - &shared_image_manager_, nullptr); + ASSERT_NO_FATAL_FAILURE(InitializeContext(GrContextType::kVulkan)); backing_factory_ = std::make_unique<ExternalVkImageBackingFactory>(context_state_); #endif } - - std::unique_ptr<VulkanImplementation> vulkan_implementation_; - scoped_refptr<viz::VulkanInProcessContextProvider> vulkan_context_provider_; - - scoped_refptr<gl::GLSurface> gl_surface_; - scoped_refptr<gl::GLContext> gl_context_; - scoped_refptr<SharedContextState> context_state_; - - SharedImageManager shared_image_manager_; - std::unique_ptr<MemoryTypeTracker> memory_type_tracker_; - std::unique_ptr<SharedImageRepresentationFactory> - shared_image_representation_factory_; - std::unique_ptr<ExternalVkImageBackingFactory> backing_factory_; }; #if BUILDFLAG(USE_DAWN) @@ -184,14 +120,12 @@ ASSERT_NE(backing, nullptr); std::unique_ptr<SharedImageRepresentationFactoryRef> factory_ref = - shared_image_manager_.Register(std::move(backing), - memory_type_tracker_.get()); + shared_image_manager_.Register(std::move(backing), &memory_type_tracker_); { // Create a Dawn representation to clear the texture contents to a green. - auto dawn_representation = - shared_image_representation_factory_->ProduceDawn( - mailbox, dawn_device_.Get(), WGPUBackendType_Vulkan, {}); + auto dawn_representation = shared_image_representation_factory_.ProduceDawn( + mailbox, dawn_device_.Get(), WGPUBackendType_Vulkan, {}); ASSERT_TRUE(dawn_representation); auto dawn_scoped_access = dawn_representation->BeginScopedAccess( @@ -224,9 +158,8 @@ EXPECT_TRUE(factory_ref->IsCleared()); { - auto skia_representation = - shared_image_representation_factory_->ProduceSkia(mailbox, - context_state_.get()); + auto skia_representation = shared_image_representation_factory_.ProduceSkia( + mailbox, context_state_.get()); std::vector<GrBackendSemaphore> begin_semaphores; std::vector<GrBackendSemaphore> end_semaphores; @@ -302,14 +235,12 @@ ASSERT_NE(backing, nullptr); std::unique_ptr<SharedImageRepresentationFactoryRef> factory_ref = - shared_image_manager_.Register(std::move(backing), - memory_type_tracker_.get()); + shared_image_manager_.Register(std::move(backing), &memory_type_tracker_); { // Create a SkiaImageRepresentation - auto skia_representation = - shared_image_representation_factory_->ProduceSkia(mailbox, - context_state_.get()); + auto skia_representation = shared_image_representation_factory_.ProduceSkia( + mailbox, context_state_.get()); // Begin access for writing std::vector<GrBackendSemaphore> begin_semaphores; @@ -347,9 +278,8 @@ { // Create a Dawn representation - auto dawn_representation = - shared_image_representation_factory_->ProduceDawn( - mailbox, dawn_device_.Get(), WGPUBackendType_Vulkan, {}); + auto dawn_representation = shared_image_representation_factory_.ProduceDawn( + mailbox, dawn_device_.Get(), WGPUBackendType_Vulkan, {}); ASSERT_TRUE(dawn_representation); // Begin access to copy the data out. Skia should have initialized the @@ -445,7 +375,7 @@ SkAlphaType alpha_type = kPremul_SkAlphaType; uint32_t usage = SHARED_IMAGE_USAGE_DISPLAY_READ | SHARED_IMAGE_USAGE_GLES2; - bool supported = backing_factory_->IsSupported( + bool supported = backing_factory_->CanCreateSharedImage( usage, format, size, /*thread_safe=*/false, gfx::EMPTY_BUFFER, GrContextType::kVulkan, {}); ASSERT_TRUE(supported); @@ -457,11 +387,10 @@ ASSERT_TRUE(backing); std::unique_ptr<SharedImageRepresentationFactoryRef> shared_image = - shared_image_manager_.Register(std::move(backing), - memory_type_tracker_.get()); + shared_image_manager_.Register(std::move(backing), &memory_type_tracker_); EXPECT_TRUE(shared_image); - auto skia_representation = shared_image_representation_factory_->ProduceSkia( + auto skia_representation = shared_image_representation_factory_.ProduceSkia( mailbox, context_state_.get()); ASSERT_TRUE(skia_representation); @@ -532,7 +461,7 @@ // Verify GL access works. if (use_passthrough()) { auto gl_representation = - shared_image_representation_factory_->ProduceGLTexturePassthrough( + shared_image_representation_factory_.ProduceGLTexturePassthrough( mailbox); ASSERT_TRUE(gl_representation); auto scoped_access = gl_representation->BeginScopedAccess( @@ -545,7 +474,7 @@ EXPECT_NE(texture->service_id(), 0u); } else { auto gl_representation = - shared_image_representation_factory_->ProduceGLTexture(mailbox); + shared_image_representation_factory_.ProduceGLTexture(mailbox); ASSERT_TRUE(gl_representation); auto scoped_access = gl_representation->BeginScopedAccess( GL_SHARED_IMAGE_ACCESS_MODE_READ_CHROMIUM,
diff --git a/gpu/command_buffer/service/shared_image/gl_texture_image_backing_factory_unittest.cc b/gpu/command_buffer/service/shared_image/gl_texture_image_backing_factory_unittest.cc index d31be8e0..5ba4a05 100644 --- a/gpu/command_buffer/service/shared_image/gl_texture_image_backing_factory_unittest.cc +++ b/gpu/command_buffer/service/shared_image/gl_texture_image_backing_factory_unittest.cc
@@ -23,6 +23,7 @@ #include "gpu/command_buffer/service/shared_image/shared_image_format_utils.h" #include "gpu/command_buffer/service/shared_image/shared_image_manager.h" #include "gpu/command_buffer/service/shared_image/shared_image_representation.h" +#include "gpu/command_buffer/service/shared_image/shared_image_test_base.h" #include "gpu/command_buffer/service/texture_manager.h" #include "gpu/config/gpu_driver_bug_workarounds.h" #include "gpu/config/gpu_feature_info.h" @@ -36,9 +37,6 @@ #include "third_party/skia/include/gpu/GrBackendSurface.h" #include "ui/gfx/color_space.h" #include "ui/gfx/gpu_memory_buffer.h" -#include "ui/gl/gl_surface.h" -#include "ui/gl/gl_utils.h" -#include "ui/gl/init/gl_factory.h" #include "ui/gl/progress_reporter.h" using testing::AtLeast; @@ -73,43 +71,11 @@ return bitmaps; } -std::vector<SkPixmap> GetSkPixmaps(const std::vector<SkBitmap>& bitmaps) { - std::vector<SkPixmap> pixmaps; - for (auto& bitmap : bitmaps) { - pixmaps.push_back(bitmap.pixmap()); - } - return pixmaps; -} - bool IsGLSupported(viz::SharedImageFormat format) { return format.is_single_plane() && !format.IsLegacyMultiplanar() && format != viz::SinglePlaneFormat::kBGR_565; } -void CreateSharedContext(const GpuDriverBugWorkarounds& workarounds, - scoped_refptr<gl::GLSurface>& surface, - scoped_refptr<gl::GLContext>& context, - scoped_refptr<SharedContextState>& context_state, - scoped_refptr<gles2::FeatureInfo>& feature_info) { - surface = - gl::init::CreateOffscreenGLSurface(gl::GetDefaultDisplay(), gfx::Size()); - ASSERT_TRUE(surface); - context = - gl::init::CreateGLContext(nullptr, surface.get(), gl::GLContextAttribs()); - ASSERT_TRUE(context); - bool result = context->MakeCurrent(surface.get()); - ASSERT_TRUE(result); - - scoped_refptr<gl::GLShareGroup> share_group = new gl::GLShareGroup(); - feature_info = - base::MakeRefCounted<gles2::FeatureInfo>(workarounds, GpuFeatureInfo()); - context_state = base::MakeRefCounted<SharedContextState>( - std::move(share_group), surface, context, - /*use_virtualized_gl_contexts=*/false, base::DoNothing()); - context_state->InitializeGrContext(GpuPreferences(), workarounds, nullptr); - context_state->InitializeGL(GpuPreferences(), feature_info); -} - class MockProgressReporter : public gl::ProgressReporter { public: MockProgressReporter() = default; @@ -119,21 +85,14 @@ MOCK_METHOD0(ReportProgress, void()); }; -class GLTextureImageBackingFactoryTestBase : public testing::Test { +class GLTextureImageBackingFactoryTestBase : public SharedImageTestBase { public: - explicit GLTextureImageBackingFactoryTestBase(bool is_thread_safe) - : shared_image_manager_( - std::make_unique<SharedImageManager>(is_thread_safe)) {} - ~GLTextureImageBackingFactoryTestBase() override { - // |context_state_| must be destroyed on its own context. - context_state_->MakeCurrent(surface_.get(), /*needs_gl=*/true); - } + GLTextureImageBackingFactoryTestBase() = default; + ~GLTextureImageBackingFactoryTestBase() override = default; - void SetUpBase(const GpuDriverBugWorkarounds& workarounds, - bool for_cpu_upload_usage) { - scoped_refptr<gles2::FeatureInfo> feature_info; - CreateSharedContext(workarounds, surface_, context_, context_state_, - feature_info); + void SetUpBase(bool for_cpu_upload_usage) { + ASSERT_NO_FATAL_FAILURE(InitializeContext(GrContextType::kGL)); + auto* feature_info = context_state_->feature_info(); // Check if platform should support various formats. supports_r_rg_ = @@ -153,22 +112,9 @@ supports_ar30_ = feature_info->feature_flags().chromium_image_ar30; supports_ab30_ = feature_info->feature_flags().chromium_image_ab30; - GpuPreferences preferences; - preferences.use_passthrough_cmd_decoder = use_passthrough(); backing_factory_ = std::make_unique<GLTextureImageBackingFactory>( - preferences, workarounds, context_state_->feature_info(), + gpu_preferences_, gpu_workarounds_, context_state_->feature_info(), &progress_reporter_, for_cpu_upload_usage); - - memory_type_tracker_ = std::make_unique<MemoryTypeTracker>(nullptr); - shared_image_representation_factory_ = - std::make_unique<SharedImageRepresentationFactory>( - shared_image_manager_.get(), nullptr); - } - - bool use_passthrough() const { - return gles2::UsePassthroughCommandDecoder( - base::CommandLine::ForCurrentProcess()) && - gles2::PassthroughCommandDecoderSupported(); } bool IsFormatSupport(viz::SharedImageFormat format) const { @@ -197,14 +143,6 @@ protected: ::testing::NiceMock<MockProgressReporter> progress_reporter_; - scoped_refptr<gl::GLSurface> surface_; - scoped_refptr<gl::GLContext> context_; - scoped_refptr<SharedContextState> context_state_; - std::unique_ptr<GLTextureImageBackingFactory> backing_factory_; - std::unique_ptr<SharedImageManager> shared_image_manager_; - std::unique_ptr<MemoryTypeTracker> memory_type_tracker_; - std::unique_ptr<SharedImageRepresentationFactory> - shared_image_representation_factory_; bool supports_r_rg_ = false; bool supports_rg16_ = false; bool supports_rgba_f16_ = false; @@ -217,12 +155,7 @@ class GLTextureImageBackingFactoryTest : public GLTextureImageBackingFactoryTestBase { public: - GLTextureImageBackingFactoryTest() - : GLTextureImageBackingFactoryTestBase(false) {} - void SetUp() override { - GpuDriverBugWorkarounds workarounds; - SetUpBase(workarounds, /*for_cpu_upload_usage=*/false); - } + void SetUp() override { SetUpBase(/*for_cpu_upload_usage=*/false); } }; // SharedImageFormat parameterized tests. @@ -244,12 +177,7 @@ : public GLTextureImageBackingFactoryTestBase, public testing::WithParamInterface<viz::SharedImageFormat> { public: - GLTextureImageBackingFactoryWithUploadTest() - : GLTextureImageBackingFactoryTestBase(false) {} - void SetUp() override { - GpuDriverBugWorkarounds workarounds; - SetUpBase(workarounds, /*for_cpu_upload_usage=*/true); - } + void SetUp() override { SetUpBase(/*for_cpu_upload_usage=*/true); } viz::SharedImageFormat get_format() { return GetParam(); } }; @@ -293,9 +221,8 @@ EXPECT_GT(backing_estimated_size, 0u); std::unique_ptr<SharedImageRepresentationFactoryRef> shared_image = - shared_image_manager_->Register(std::move(backing), - memory_type_tracker_.get()); - EXPECT_EQ(backing_estimated_size, memory_type_tracker_->GetMemRepresented()); + shared_image_manager_.Register(std::move(backing), &memory_type_tracker_); + EXPECT_EQ(backing_estimated_size, memory_type_tracker_.GetMemRepresented()); shared_image.reset(); } @@ -408,13 +335,12 @@ // First, validate via a GLTextureImageRepresentation. std::unique_ptr<SharedImageRepresentationFactoryRef> shared_image = - shared_image_manager_->Register(std::move(backing), - memory_type_tracker_.get()); + shared_image_manager_.Register(std::move(backing), &memory_type_tracker_); EXPECT_TRUE(shared_image); GLenum expected_target = GL_TEXTURE_2D; if (!use_passthrough()) { auto gl_representation = - shared_image_representation_factory_->ProduceGLTexture(mailbox); + shared_image_representation_factory_.ProduceGLTexture(mailbox); EXPECT_TRUE(gl_representation); auto* texture = gl_representation->GetTexture(/*plane_index=*/0); EXPECT_TRUE(texture->service_id()); @@ -429,7 +355,7 @@ // Next, validate a GLTexturePassthroughImageRepresentation. if (use_passthrough()) { auto gl_representation = - shared_image_representation_factory_->ProduceGLTexturePassthrough( + shared_image_representation_factory_.ProduceGLTexturePassthrough( mailbox); EXPECT_TRUE(gl_representation); auto texture = gl_representation->GetTexturePassthrough(/*plane_index=*/0); @@ -443,7 +369,7 @@ } // Finally, validate a SkiaImageRepresentation. - auto skia_representation = shared_image_representation_factory_->ProduceSkia( + auto skia_representation = shared_image_representation_factory_.ProduceSkia( mailbox, context_state_.get()); EXPECT_TRUE(skia_representation); std::vector<GrBackendSemaphore> begin_semaphores; @@ -535,13 +461,12 @@ // Validate via a GLTextureImageRepresentation(Passthrough). std::unique_ptr<SharedImageRepresentationFactoryRef> shared_image = - shared_image_manager_->Register(std::move(backing), - memory_type_tracker_.get()); + shared_image_manager_.Register(std::move(backing), &memory_type_tracker_); EXPECT_TRUE(shared_image); GLenum expected_target = GL_TEXTURE_2D; if (!use_passthrough()) { auto gl_representation = - shared_image_representation_factory_->ProduceGLTexture(mailbox); + shared_image_representation_factory_.ProduceGLTexture(mailbox); EXPECT_TRUE(gl_representation); EXPECT_TRUE(gl_representation->GetTexture()->service_id()); EXPECT_EQ(expected_target, gl_representation->GetTexture()->target()); @@ -552,7 +477,7 @@ gl_representation.reset(); } else { auto gl_representation = - shared_image_representation_factory_->ProduceGLTexturePassthrough( + shared_image_representation_factory_.ProduceGLTexturePassthrough( mailbox); EXPECT_TRUE(gl_representation); EXPECT_TRUE(gl_representation->GetTexturePassthrough()->service_id());
diff --git a/gpu/command_buffer/service/shared_image/iosurface_image_backing_factory_unittest.cc b/gpu/command_buffer/service/shared_image/iosurface_image_backing_factory_unittest.cc index 2073a52..b8fda39d 100644 --- a/gpu/command_buffer/service/shared_image/iosurface_image_backing_factory_unittest.cc +++ b/gpu/command_buffer/service/shared_image/iosurface_image_backing_factory_unittest.cc
@@ -16,6 +16,7 @@ #include "gpu/command_buffer/service/shared_image/shared_image_format_utils.h" #include "gpu/command_buffer/service/shared_image/shared_image_manager.h" #include "gpu/command_buffer/service/shared_image/shared_image_representation.h" +#include "gpu/command_buffer/service/shared_image/shared_image_test_base.h" #include "gpu/config/gpu_driver_bug_workarounds.h" #include "gpu/config/gpu_feature_info.h" #include "gpu/config/gpu_preferences.h" @@ -30,10 +31,7 @@ #include "third_party/skia/include/gpu/GrBackendSurface.h" #include "third_party/skia/include/gpu/ganesh/SkImageGanesh.h" #include "ui/gl/buildflags.h" -#include "ui/gl/gl_context.h" -#include "ui/gl/gl_surface.h" -#include "ui/gl/gl_utils.h" -#include "ui/gl/init/gl_factory.h" +#include "ui/gl/gl_bindings.h" #include "ui/gl/progress_reporter.h" #if BUILDFLAG(USE_DAWN) @@ -54,62 +52,27 @@ } // namespace -class IOSurfaceImageBackingFactoryTest : public testing::Test { +class IOSurfaceImageBackingFactoryTest : public SharedImageTestBase { public: void SetUp() override { - surface_ = gl::init::CreateOffscreenGLSurface(gl::GetDefaultDisplayEGL(), - gfx::Size()); - ASSERT_TRUE(surface_); - context_ = gl::init::CreateGLContext(nullptr, surface_.get(), - gl::GLContextAttribs()); - ASSERT_TRUE(context_); - bool result = context_->MakeCurrent(surface_.get()); - ASSERT_TRUE(result); - - GpuPreferences preferences; - preferences.use_passthrough_cmd_decoder = true; - preferences.texture_target_exception_list.push_back( + ASSERT_TRUE(gpu_preferences_.use_passthrough_cmd_decoder); + gpu_preferences_.texture_target_exception_list.push_back( gfx::BufferUsageAndFormat(gfx::BufferUsage::SCANOUT, gfx::BufferFormat::RGBA_8888)); - GpuDriverBugWorkarounds workarounds; - scoped_refptr<gl::GLShareGroup> share_group = new gl::GLShareGroup(); - context_state_ = base::MakeRefCounted<SharedContextState>( - std::move(share_group), surface_, context_, - /*use_virtualized_gl_contexts=*/false, base::DoNothing()); - context_state_->InitializeGrContext(preferences, workarounds, nullptr); - auto feature_info = - base::MakeRefCounted<gles2::FeatureInfo>(workarounds, GpuFeatureInfo()); - context_state_->InitializeGL(preferences, std::move(feature_info)); + ASSERT_NO_FATAL_FAILURE(InitializeContext(GrContextType::kGL)); backing_factory_ = std::make_unique<IOSurfaceImageBackingFactory>( - preferences, workarounds, context_state_->feature_info(), + gpu_preferences_, gpu_workarounds_, context_state_->feature_info(), /*progress_reporter=*/nullptr); - - memory_type_tracker_ = std::make_unique<MemoryTypeTracker>(nullptr); - shared_image_representation_factory_ = - std::make_unique<SharedImageRepresentationFactory>( - &shared_image_manager_, nullptr); } - GrDirectContext* gr_context() { return context_state_->gr_context(); } - protected: - scoped_refptr<gl::GLSurface> surface_; - scoped_refptr<gl::GLContext> context_; - scoped_refptr<SharedContextState> context_state_; - std::unique_ptr<IOSurfaceImageBackingFactory> backing_factory_; - SharedImageManager shared_image_manager_; - std::unique_ptr<MemoryTypeTracker> memory_type_tracker_; - std::unique_ptr<SharedImageRepresentationFactory> - shared_image_representation_factory_; - void CheckSkiaPixels(const Mailbox& mailbox, const gfx::Size& size, const std::vector<uint8_t> expected_color) { - auto skia_representation = - shared_image_representation_factory_->ProduceSkia(mailbox, - context_state_); + auto skia_representation = shared_image_representation_factory_.ProduceSkia( + mailbox, context_state_); ASSERT_NE(skia_representation, nullptr); std::unique_ptr<SkiaImageRepresentation::ScopedReadAccess> @@ -172,13 +135,12 @@ GLenum expected_target = gpu::GetPlatformSpecificTextureTarget(); std::unique_ptr<SharedImageRepresentationFactoryRef> factory_ref = - shared_image_manager_.Register(std::move(backing), - memory_type_tracker_.get()); + shared_image_manager_.Register(std::move(backing), &memory_type_tracker_); // Create a GLTextureImageRepresentation. { auto gl_representation = - shared_image_representation_factory_->ProduceGLTexturePassthrough( + shared_image_representation_factory_.ProduceGLTexturePassthrough( mailbox); EXPECT_TRUE(gl_representation); EXPECT_EQ(expected_target, @@ -251,11 +213,10 @@ EXPECT_TRUE(backing); std::unique_ptr<SharedImageRepresentationFactoryRef> factory_ref = - shared_image_manager_.Register(std::move(backing), - memory_type_tracker_.get()); + shared_image_manager_.Register(std::move(backing), &memory_type_tracker_); // Create a DawnImageRepresentation. - auto dawn_representation = shared_image_representation_factory_->ProduceDawn( + auto dawn_representation = shared_image_representation_factory_.ProduceDawn( mailbox, device.Get(), WGPUBackendType_Metal, {}); EXPECT_TRUE(dawn_representation); @@ -319,13 +280,12 @@ GLenum expected_target = GL_TEXTURE_RECTANGLE; std::unique_ptr<SharedImageRepresentationFactoryRef> factory_ref = - shared_image_manager_.Register(std::move(backing), - memory_type_tracker_.get()); + shared_image_manager_.Register(std::move(backing), &memory_type_tracker_); { // Create a GLTextureImageRepresentation. auto gl_representation = - shared_image_representation_factory_->ProduceGLTexturePassthrough( + shared_image_representation_factory_.ProduceGLTexturePassthrough( mailbox); EXPECT_TRUE(gl_representation); EXPECT_EQ(expected_target, @@ -380,9 +340,8 @@ DawnProcTable procs = dawn::native::GetProcs(); dawnProcSetProcs(&procs); { - auto dawn_representation = - shared_image_representation_factory_->ProduceDawn( - mailbox, device.Get(), WGPUBackendType_Metal, {}); + auto dawn_representation = shared_image_representation_factory_.ProduceDawn( + mailbox, device.Get(), WGPUBackendType_Metal, {}); ASSERT_TRUE(dawn_representation); auto dawn_scoped_access = dawn_representation->BeginScopedAccess( @@ -444,8 +403,7 @@ ASSERT_NE(backing, nullptr); std::unique_ptr<SharedImageRepresentationFactoryRef> factory_ref = - shared_image_manager_.Register(std::move(backing), - memory_type_tracker_.get()); + shared_image_manager_.Register(std::move(backing), &memory_type_tracker_); // Create dawn device dawn::native::Instance instance; @@ -470,9 +428,8 @@ DawnProcTable procs = dawn::native::GetProcs(); dawnProcSetProcs(&procs); { - auto dawn_representation = - shared_image_representation_factory_->ProduceDawn( - mailbox, device.Get(), WGPUBackendType_Metal, {}); + auto dawn_representation = shared_image_representation_factory_.ProduceDawn( + mailbox, device.Get(), WGPUBackendType_Metal, {}); ASSERT_TRUE(dawn_representation); auto dawn_scoped_access = dawn_representation->BeginScopedAccess( @@ -509,8 +466,8 @@ EXPECT_FALSE(factory_ref->IsCleared()); // Produce skia representation - auto skia_representation = shared_image_representation_factory_->ProduceSkia( - mailbox, context_state_); + auto skia_representation = + shared_image_representation_factory_.ProduceSkia(mailbox, context_state_); ASSERT_NE(skia_representation, nullptr); // Expect BeginScopedReadAccess to fail because sharedImage is uninitialized @@ -538,11 +495,10 @@ ASSERT_NE(backing, nullptr); std::unique_ptr<SharedImageRepresentationFactoryRef> factory_ref = - shared_image_manager_.Register(std::move(backing), - memory_type_tracker_.get()); + shared_image_manager_.Register(std::move(backing), &memory_type_tracker_); - auto skia_representation = shared_image_representation_factory_->ProduceSkia( - mailbox, context_state_); + auto skia_representation = + shared_image_representation_factory_.ProduceSkia(mailbox, context_state_); ASSERT_NE(skia_representation, nullptr); EXPECT_FALSE(skia_representation->IsCleared()); @@ -553,30 +509,6 @@ EXPECT_EQ(scoped_read_access, nullptr); } -void CreateSharedContext(const GpuDriverBugWorkarounds& workarounds, - scoped_refptr<gl::GLSurface>& surface, - scoped_refptr<gl::GLContext>& context, - scoped_refptr<SharedContextState>& context_state, - scoped_refptr<gles2::FeatureInfo>& feature_info) { - surface = - gl::init::CreateOffscreenGLSurface(gl::GetDefaultDisplay(), gfx::Size()); - ASSERT_TRUE(surface); - context = - gl::init::CreateGLContext(nullptr, surface.get(), gl::GLContextAttribs()); - ASSERT_TRUE(context); - bool result = context->MakeCurrent(surface.get()); - ASSERT_TRUE(result); - - scoped_refptr<gl::GLShareGroup> share_group = new gl::GLShareGroup(); - feature_info = - base::MakeRefCounted<gles2::FeatureInfo>(workarounds, GpuFeatureInfo()); - context_state = base::MakeRefCounted<SharedContextState>( - std::move(share_group), surface, context, - /*use_virtualized_gl_contexts=*/false, base::DoNothing()); - context_state->InitializeGrContext(GpuPreferences(), workarounds, nullptr); - context_state->InitializeGL(GpuPreferences(), feature_info); -} - class MockProgressReporter : public gl::ProgressReporter { public: MockProgressReporter() = default; @@ -587,21 +519,18 @@ }; class IOSurfaceImageBackingFactoryWithFormatTestBase - : public testing::TestWithParam<viz::SharedImageFormat> { + : public SharedImageTestBase, + public testing::WithParamInterface<viz::SharedImageFormat> { public: - explicit IOSurfaceImageBackingFactoryWithFormatTestBase() - : shared_image_manager_( - std::make_unique<SharedImageManager>(/*thread_safe=*/false)) {} - ~IOSurfaceImageBackingFactoryWithFormatTestBase() override { - // |context_state_| must be destroyed on its own context. - context_state_->MakeCurrent(surface_.get(), /*needs_gl=*/true); - } + IOSurfaceImageBackingFactoryWithFormatTestBase() = default; + ~IOSurfaceImageBackingFactoryWithFormatTestBase() override = default; void SetUp() override { - GpuDriverBugWorkarounds workarounds; - scoped_refptr<gles2::FeatureInfo> feature_info; - CreateSharedContext(workarounds, surface_, context_, context_state_, - feature_info); + ASSERT_TRUE(gpu_preferences_.use_passthrough_cmd_decoder); + + ASSERT_NO_FATAL_FAILURE(InitializeContext(GrContextType::kGL)); + auto* feature_info = context_state_->feature_info(); + supports_etc1_ = feature_info->validators()->compressed_texture_format.IsValid( GL_ETC1_RGB8_OES); @@ -612,30 +541,15 @@ supports_ycbcr_p010_ = feature_info->feature_flags().chromium_image_ycbcr_p010; - GpuPreferences preferences; - preferences.use_passthrough_cmd_decoder = true; backing_factory_ = std::make_unique<IOSurfaceImageBackingFactory>( - preferences, workarounds, context_state_->feature_info(), + gpu_preferences_, gpu_workarounds_, context_state_->feature_info(), &progress_reporter_); - - memory_type_tracker_ = std::make_unique<MemoryTypeTracker>(nullptr); - shared_image_representation_factory_ = - std::make_unique<SharedImageRepresentationFactory>( - shared_image_manager_.get(), nullptr); } viz::SharedImageFormat get_format() { return GetParam(); } protected: ::testing::NiceMock<MockProgressReporter> progress_reporter_; - scoped_refptr<gl::GLSurface> surface_; - scoped_refptr<gl::GLContext> context_; - scoped_refptr<SharedContextState> context_state_; - std::unique_ptr<IOSurfaceImageBackingFactory> backing_factory_; - std::unique_ptr<SharedImageManager> shared_image_manager_; - std::unique_ptr<MemoryTypeTracker> memory_type_tracker_; - std::unique_ptr<SharedImageRepresentationFactory> - shared_image_representation_factory_; bool supports_etc1_ = false; bool supports_ar30_ = false; bool supports_ab30_ = false; @@ -691,12 +605,11 @@ // First, validate a GLTexturePassthroughImageRepresentation. std::unique_ptr<SharedImageRepresentationFactoryRef> shared_image = - shared_image_manager_->Register(std::move(backing), - memory_type_tracker_.get()); + shared_image_manager_.Register(std::move(backing), &memory_type_tracker_); EXPECT_TRUE(shared_image); { auto gl_representation = - shared_image_representation_factory_->ProduceGLTexturePassthrough( + shared_image_representation_factory_.ProduceGLTexturePassthrough( mailbox); EXPECT_TRUE(gl_representation); EXPECT_TRUE(gl_representation->GetTexturePassthrough()->service_id()); @@ -713,7 +626,7 @@ } // Finally, validate a SkiaImageRepresentation. - auto skia_representation = shared_image_representation_factory_->ProduceSkia( + auto skia_representation = shared_image_representation_factory_.ProduceSkia( mailbox, context_state_.get()); EXPECT_TRUE(skia_representation); std::vector<GrBackendSemaphore> begin_semaphores; @@ -777,14 +690,13 @@ // Validate via a GLTextureImageRepresentation(Passthrough). std::unique_ptr<SharedImageRepresentationFactoryRef> shared_image = - shared_image_manager_->Register(std::move(backing), - memory_type_tracker_.get()); + shared_image_manager_.Register(std::move(backing), &memory_type_tracker_); EXPECT_TRUE(shared_image); GLenum expected_target = gpu::GetPlatformSpecificTextureTarget(); { auto gl_representation = - shared_image_representation_factory_->ProduceGLTexturePassthrough( + shared_image_representation_factory_.ProduceGLTexturePassthrough( mailbox); EXPECT_TRUE(gl_representation); EXPECT_TRUE(gl_representation->GetTexturePassthrough()->service_id()); @@ -822,12 +734,11 @@ // Validate via a GLTextureImageRepresentation(Passthrough). std::unique_ptr<SharedImageRepresentationFactoryRef> shared_image = - shared_image_manager_->Register(std::move(backing), - memory_type_tracker_.get()); + shared_image_manager_.Register(std::move(backing), &memory_type_tracker_); EXPECT_TRUE(shared_image); { auto gl_representation = - shared_image_representation_factory_->ProduceGLTexturePassthrough( + shared_image_representation_factory_.ProduceGLTexturePassthrough( mailbox); EXPECT_TRUE(gl_representation); EXPECT_TRUE(gl_representation->GetTexturePassthrough()->service_id()); @@ -923,9 +834,8 @@ EXPECT_GT(backing_estimated_size, 0u); std::unique_ptr<SharedImageRepresentationFactoryRef> shared_image = - shared_image_manager_->Register(std::move(backing), - memory_type_tracker_.get()); - EXPECT_EQ(backing_estimated_size, memory_type_tracker_->GetMemRepresented()); + shared_image_manager_.Register(std::move(backing), &memory_type_tracker_); + EXPECT_EQ(backing_estimated_size, memory_type_tracker_.GetMemRepresented()); shared_image.reset(); } @@ -1035,12 +945,11 @@ // First, validate a GLTexturePassthroughImageRepresentation. std::unique_ptr<SharedImageRepresentationFactoryRef> shared_image = - shared_image_manager_->Register(std::move(backing), - memory_type_tracker_.get()); + shared_image_manager_.Register(std::move(backing), &memory_type_tracker_); EXPECT_TRUE(shared_image); { auto gl_representation = - shared_image_representation_factory_->ProduceGLTexturePassthrough( + shared_image_representation_factory_.ProduceGLTexturePassthrough( mailbox); EXPECT_TRUE(gl_representation); for (int plane = 0; plane < format.NumberOfPlanes(); plane++) { @@ -1055,7 +964,7 @@ } // Finally, validate a SkiaImageRepresentation. - auto skia_representation = shared_image_representation_factory_->ProduceSkia( + auto skia_representation = shared_image_representation_factory_.ProduceSkia( mailbox, context_state_.get()); EXPECT_TRUE(skia_representation); std::vector<GrBackendSemaphore> begin_semaphores;
diff --git a/gpu/command_buffer/service/shared_image/shared_image_test_base.cc b/gpu/command_buffer/service/shared_image/shared_image_test_base.cc new file mode 100644 index 0000000..4554f3a5 --- /dev/null +++ b/gpu/command_buffer/service/shared_image/shared_image_test_base.cc
@@ -0,0 +1,107 @@ +// 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 "gpu/command_buffer/service/shared_image/shared_image_test_base.h" + +#include "base/command_line.h" +#include "gpu/command_buffer/service/service_utils.h" +#include "ui/gl/gl_context.h" +#include "ui/gl/gl_surface.h" +#include "ui/gl/gl_utils.h" +#include "ui/gl/init/gl_factory.h" + +#if BUILDFLAG(ENABLE_VULKAN) +#include "components/viz/common/gpu/vulkan_in_process_context_provider.h" +#include "gpu/vulkan/init/vulkan_factory.h" +#include "gpu/vulkan/vulkan_implementation.h" +#endif + +namespace gpu { + +// static +SkBitmap SharedImageTestBase::MakeRedBitmap(SkColorType color_type, + const gfx::Size& size) { + SkBitmap bitmap; + bitmap.allocPixels(SkImageInfo::Make(size.width(), size.height(), color_type, + kOpaque_SkAlphaType)); + + bitmap.eraseColor(SK_ColorRED); + return bitmap; +} + +// static +std::vector<SkPixmap> SharedImageTestBase::GetSkPixmaps( + const std::vector<SkBitmap>& bitmaps) { + std::vector<SkPixmap> pixmaps; + for (auto& bitmap : bitmaps) { + pixmaps.push_back(bitmap.pixmap()); + } + return pixmaps; +} + +SharedImageTestBase::SharedImageTestBase() { + gpu_preferences_.use_passthrough_cmd_decoder = + gles2::UsePassthroughCommandDecoder( + base::CommandLine::ForCurrentProcess()) && + gles2::PassthroughCommandDecoderSupported(); +} + +SharedImageTestBase::~SharedImageTestBase() { + if (context_state_) { + // |context_state_| must be destroyed while current. + context_state_->MakeCurrent(gl_surface_.get(), /*needs_gl=*/true); + } +} + +bool SharedImageTestBase::use_passthrough() const { + return gpu_preferences_.use_passthrough_cmd_decoder; +} + +GrDirectContext* SharedImageTestBase::gr_context() { + return context_state_->gr_context(); +} + +void SharedImageTestBase::InitializeContext(GrContextType context_type) { + if (context_type == GrContextType::kVulkan) { +#if BUILDFLAG(ENABLE_VULKAN) + vulkan_implementation_ = gpu::CreateVulkanImplementation(); + ASSERT_TRUE(vulkan_implementation_); + ASSERT_TRUE(vulkan_implementation_->InitializeVulkanInstance()); + vulkan_context_provider_ = viz::VulkanInProcessContextProvider::Create( + vulkan_implementation_.get()); + ASSERT_TRUE(vulkan_context_provider_); +#else + FAIL() << "Vulkan not available"; +#endif + } + + // Set up a GL context. Even if the GrContext is Vulkan it's still needed. + gl_surface_ = gl::init::CreateOffscreenGLSurface(gl::GetDefaultDisplayEGL(), + gfx::Size()); + ASSERT_TRUE(gl_surface_); + gl_context_ = gl::init::CreateGLContext(nullptr, gl_surface_.get(), + gl::GLContextAttribs()); + ASSERT_TRUE(gl_context_); + bool make_current_result = gl_context_->MakeCurrent(gl_surface_.get()); + ASSERT_TRUE(make_current_result); + + context_state_ = base::MakeRefCounted<SharedContextState>( + base::MakeRefCounted<gl::GLShareGroup>(), gl_surface_, gl_context_, + /*use_virtualized_gl_contexts=*/false, base::DoNothing(), context_type, +#if BUILDFLAG(ENABLE_VULKAN) + vulkan_context_provider_.get() +#endif + ); + + bool initialize_gl = context_state_->InitializeGL( + gpu_preferences_, base::MakeRefCounted<gles2::FeatureInfo>( + gpu_workarounds_, GpuFeatureInfo())); + ASSERT_TRUE(initialize_gl); + + bool initialize_gr = context_state_->InitializeGrContext( + gpu_preferences_, gpu_workarounds_, nullptr); + ASSERT_TRUE(initialize_gr); +} + +} // namespace gpu
diff --git a/gpu/command_buffer/service/shared_image/shared_image_test_base.h b/gpu/command_buffer/service/shared_image/shared_image_test_base.h new file mode 100644 index 0000000..bcc51531 --- /dev/null +++ b/gpu/command_buffer/service/shared_image/shared_image_test_base.h
@@ -0,0 +1,73 @@ +// 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 GPU_COMMAND_BUFFER_SERVICE_SHARED_IMAGE_SHARED_IMAGE_TEST_BASE_H_ +#define GPU_COMMAND_BUFFER_SERVICE_SHARED_IMAGE_SHARED_IMAGE_TEST_BASE_H_ + +#include "gpu/command_buffer/service/shared_context_state.h" +#include "gpu/command_buffer/service/shared_image/shared_image_backing_factory.h" +#include "gpu/command_buffer/service/shared_image/shared_image_factory.h" +#include "gpu/command_buffer/service/shared_image/shared_image_manager.h" +#include "gpu/config/gpu_driver_bug_workarounds.h" +#include "gpu/config/gpu_preferences.h" +#include "gpu/vulkan/buildflags.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/skia/include/core/SkBitmap.h" + +namespace viz { +class VulkanInProcessContextProvider; +} + +namespace gpu { + +class VulkanImplementation; + +// Test base that initializes SharedContextState and other classes needed to +// create SharedImageBackings + SharedImageBackingFactories. +class SharedImageTestBase : public testing::Test { + protected: + // Allocate a bitmap with red pixels. RED_8 will be filled with 0xFF repeating + // and RG_88 will be filled with OxFF00 repeating. + static SkBitmap MakeRedBitmap(SkColorType color_type, const gfx::Size& size); + + // Returns SkPixmap from each SkBitmap. + static std::vector<SkPixmap> GetSkPixmaps( + const std::vector<SkBitmap>& bitmaps); + + SharedImageTestBase(); + ~SharedImageTestBase() override; + + bool use_passthrough() const; + GrDirectContext* gr_context(); + + // Initializes `context_state_` for `context_type`. Expected to be called as + // part of test SetUp(). Note this function can fail with an assertion error + // so caller should wrap call in ASSERT_NO_FATAL_FAILURE() to ensure SetUp() + // exits on error. + void InitializeContext(GrContextType context_type); + + GpuPreferences gpu_preferences_; + GpuDriverBugWorkarounds gpu_workarounds_; + +#if BUILDFLAG(ENABLE_VULKAN) + std::unique_ptr<VulkanImplementation> vulkan_implementation_; + scoped_refptr<viz::VulkanInProcessContextProvider> vulkan_context_provider_; +#endif + + scoped_refptr<gl::GLSurface> gl_surface_; + scoped_refptr<gl::GLContext> gl_context_; + scoped_refptr<SharedContextState> context_state_; + + MemoryTypeTracker memory_type_tracker_{nullptr}; + SharedImageManager shared_image_manager_{/*thread_safe=*/false}; + SharedImageRepresentationFactory shared_image_representation_factory_{ + &shared_image_manager_, nullptr}; + + // To be initialized by the test implementation. + std::unique_ptr<SharedImageBackingFactory> backing_factory_; +}; + +} // namespace gpu + +#endif // GPU_COMMAND_BUFFER_SERVICE_SHARED_IMAGE_SHARED_IMAGE_TEST_BASE_H_
diff --git a/gpu/command_buffer/service/shared_image/wrapped_sk_image_backing_factory_unittest.cc b/gpu/command_buffer/service/shared_image/wrapped_sk_image_backing_factory_unittest.cc index bbeba3e..dadac79 100644 --- a/gpu/command_buffer/service/shared_image/wrapped_sk_image_backing_factory_unittest.cc +++ b/gpu/command_buffer/service/shared_image/wrapped_sk_image_backing_factory_unittest.cc
@@ -17,7 +17,7 @@ #include "gpu/command_buffer/service/shared_image/shared_image_factory.h" #include "gpu/command_buffer/service/shared_image/shared_image_manager.h" #include "gpu/command_buffer/service/shared_image/shared_image_representation.h" -#include "gpu/config/gpu_driver_bug_workarounds.h" +#include "gpu/command_buffer/service/shared_image/shared_image_test_base.h" #include "gpu/config/gpu_feature_info.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/skia/include/core/SkAlphaType.h" @@ -28,11 +28,6 @@ #include "third_party/skia/include/core/SkPromiseImageTexture.h" #include "third_party/skia/include/gpu/GrBackendSemaphore.h" #include "third_party/skia/include/gpu/ganesh/SkImageGanesh.h" -#include "ui/gl/gl_context.h" -#include "ui/gl/gl_share_group.h" -#include "ui/gl/gl_surface.h" -#include "ui/gl/gl_utils.h" -#include "ui/gl/init/gl_factory.h" namespace gpu { namespace { @@ -44,85 +39,28 @@ SHARED_IMAGE_USAGE_RASTER | SHARED_IMAGE_USAGE_CPU_UPLOAD; -// Allocate a bitmap with red pixels. RED_8 will be filled with 0xFF repeating -// and RG_88 will be filled with OxFF00 repeating. -SkBitmap MakeRedBitmap(SkColorType color_type, const gfx::Size& size) { - SkBitmap bitmap; - bitmap.allocPixels(SkImageInfo::Make(size.width(), size.height(), color_type, - kOpaque_SkAlphaType)); - - bitmap.eraseColor(SK_ColorRED); - return bitmap; -} - -std::vector<SkPixmap> GetSkPixmaps(const std::vector<SkBitmap>& bitmaps) { - std::vector<SkPixmap> pixmaps; - for (auto& bitmap : bitmaps) { - pixmaps.push_back(bitmap.pixmap()); - } - return pixmaps; -} - class WrappedSkImageBackingFactoryTest - : public testing::TestWithParam<viz::SharedImageFormat> { + : public SharedImageTestBase, + public testing::WithParamInterface<viz::SharedImageFormat> { public: WrappedSkImageBackingFactoryTest() = default; - ~WrappedSkImageBackingFactoryTest() override { - if (context_state_) { - // |context_state_| must be destroyed while current. - context_state_->MakeCurrent(surface_.get(), /*needs_gl=*/true); - } - } + ~WrappedSkImageBackingFactoryTest() override = default; viz::SharedImageFormat GetFormat() { return GetParam(); } void SetUp() override { - GpuPreferences preferences; - // We don't use WrappedSkImage with ALPHA8 if it's GL context. // Note, that `gr_context_type` is not wired right now and is always GL. if (GetFormat() == viz::SinglePlaneFormat::kALPHA_8 && - preferences.gr_context_type == GrContextType::kGL) { + gpu_preferences_.gr_context_type == GrContextType::kGL) { GTEST_SKIP(); } - surface_ = gl::init::CreateOffscreenGLSurface(gl::GetDefaultDisplay(), - gfx::Size()); - ASSERT_TRUE(surface_); - - auto context = gl::init::CreateGLContext(nullptr, surface_.get(), - gl::GLContextAttribs()); - ASSERT_TRUE(context); - bool result = context->MakeCurrent(surface_.get()); - ASSERT_TRUE(result); - - context_state_ = base::MakeRefCounted<SharedContextState>( - base::MakeRefCounted<gl::GLShareGroup>(), surface_, std::move(context), - /*use_virtualized_gl_contexts=*/false, base::DoNothing()); - - GpuDriverBugWorkarounds workarounds; - scoped_refptr<gles2::FeatureInfo> feature_info = - base::MakeRefCounted<gles2::FeatureInfo>(workarounds, GpuFeatureInfo()); - ASSERT_TRUE( - context_state_->InitializeGrContext(preferences, workarounds, nullptr)); - ASSERT_TRUE(context_state_->InitializeGL(preferences, feature_info)); + ASSERT_NO_FATAL_FAILURE(InitializeContext(GrContextType::kGL)); backing_factory_ = std::make_unique<WrappedSkImageBackingFactory>(context_state_); - - shared_image_representation_factory_ = - std::make_unique<SharedImageRepresentationFactory>( - &shared_image_manager_, nullptr); } - - protected: - MemoryTypeTracker memory_type_tracker_{nullptr}; - SharedImageManager shared_image_manager_{/*thread_safe=*/false}; - scoped_refptr<gl::GLSurface> surface_; - scoped_refptr<SharedContextState> context_state_; - std::unique_ptr<WrappedSkImageBackingFactory> backing_factory_; - std::unique_ptr<SharedImageRepresentationFactory> - shared_image_representation_factory_; }; // Verify creation and Skia access works as expected. @@ -145,7 +83,7 @@ shared_image_manager_.Register(std::move(backing), &memory_type_tracker_); // Validate SkiaImageRepresentation works. - auto skia_representation = shared_image_representation_factory_->ProduceSkia( + auto skia_representation = shared_image_representation_factory_.ProduceSkia( mailbox, context_state_.get()); EXPECT_TRUE(skia_representation); @@ -213,7 +151,7 @@ shared_image_manager_.Register(std::move(backing), &memory_type_tracker_); // Validate SkiaImageRepresentation works. - auto skia_representation = shared_image_representation_factory_->ProduceSkia( + auto skia_representation = shared_image_representation_factory_.ProduceSkia( mailbox, context_state_.get()); EXPECT_TRUE(skia_representation); std::vector<GrBackendSemaphore> begin_semaphores;
diff --git a/headless/lib/browser/headless_browser_context_impl.cc b/headless/lib/browser/headless_browser_context_impl.cc index 55fb6b58c..9b7dd4c 100644 --- a/headless/lib/browser/headless_browser_context_impl.cc +++ b/headless/lib/browser/headless_browser_context_impl.cc
@@ -156,50 +156,6 @@ return result; } -void HeadlessBrowserContextImpl::SetDevToolsFrameToken( - int render_process_id, - int render_frame_routing_id, - const base::UnguessableToken& devtools_frame_token, - int frame_tree_node_id) { - base::AutoLock lock(devtools_frame_token_map_lock_); - devtools_frame_token_map_[content::GlobalRenderFrameHostId( - render_process_id, render_frame_routing_id)] = devtools_frame_token; - frame_tree_node_id_to_devtools_frame_token_map_[frame_tree_node_id] = - devtools_frame_token; -} - -void HeadlessBrowserContextImpl::RemoveDevToolsFrameToken( - int render_process_id, - int render_frame_routing_id, - int frame_tree_node_id) { - base::AutoLock lock(devtools_frame_token_map_lock_); - devtools_frame_token_map_.erase(content::GlobalRenderFrameHostId( - render_process_id, render_frame_routing_id)); - frame_tree_node_id_to_devtools_frame_token_map_.erase(frame_tree_node_id); -} - -const base::UnguessableToken* HeadlessBrowserContextImpl::GetDevToolsFrameToken( - int render_process_id, - int render_frame_id) const { - base::AutoLock lock(devtools_frame_token_map_lock_); - const auto& find_it = devtools_frame_token_map_.find( - content::GlobalRenderFrameHostId(render_process_id, render_frame_id)); - if (find_it == devtools_frame_token_map_.end()) - return nullptr; - return &find_it->second; -} - -const base::UnguessableToken* -HeadlessBrowserContextImpl::GetDevToolsFrameTokenForFrameTreeNodeId( - int frame_tree_node_id) const { - base::AutoLock lock(devtools_frame_token_map_lock_); - const auto& find_it = - frame_tree_node_id_to_devtools_frame_token_map_.find(frame_tree_node_id); - if (find_it == frame_tree_node_id_to_devtools_frame_token_map_.end()) - return nullptr; - return &find_it->second; -} - void HeadlessBrowserContextImpl::Close() { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); browser_->DestroyBrowserContext(this);
diff --git a/headless/lib/browser/headless_browser_context_impl.h b/headless/lib/browser/headless_browser_context_impl.h index 070e29b..bcd75e8 100644 --- a/headless/lib/browser/headless_browser_context_impl.h +++ b/headless/lib/browser/headless_browser_context_impl.h
@@ -54,15 +54,6 @@ void Close() override; const std::string& Id() override; - void SetDevToolsFrameToken(int render_process_id, - int render_frame_routing_id, - const base::UnguessableToken& devtools_frame_token, - int frame_tree_node_id); - - void RemoveDevToolsFrameToken(int render_process_id, - int render_frame_routing_id, - int frame_tree_node_id); - // BrowserContext implementation: std::unique_ptr<content::ZoomLevelDelegate> CreateZoomLevelDelegate( const base::FilePath& partition_path) override; @@ -100,17 +91,6 @@ HeadlessBrowserImpl* browser() const; const HeadlessBrowserContextOptions* options() const; - // Returns the DevTools frame token for the corresponding RenderFrameHost or - // null if can't be found. Can be called on any thread. - const base::UnguessableToken* GetDevToolsFrameToken( - int render_process_id, - int render_frame_id) const; - - // Returns the DevTools frame token for the corresponding frame tree node id - // or null if can't be found. Can be called on any thread. - const base::UnguessableToken* GetDevToolsFrameTokenForFrameTreeNodeId( - int frame_tree_node_id) const; - void ConfigureNetworkContextParams( bool in_memory, const base::FilePath& relative_partition_path, @@ -134,16 +114,6 @@ std::unordered_map<std::string, std::unique_ptr<HeadlessWebContents>> web_contents_map_; - // Guards |devtools_frame_token_map_| from being concurrently written on the - // UI thread and read on the IO thread. - // TODO(alexclarke): Remove if we can add DevTools frame token ID to - // ResourceRequestInfo. See https://crbug.com/715541 - mutable base::Lock devtools_frame_token_map_lock_; - base::flat_map<content::GlobalRenderFrameHostId, base::UnguessableToken> - devtools_frame_token_map_; - base::flat_map<int, base::UnguessableToken> - frame_tree_node_id_to_devtools_frame_token_map_; - std::unique_ptr<content::PermissionControllerDelegate> permission_controller_delegate_;
diff --git a/headless/lib/browser/headless_web_contents_impl.cc b/headless/lib/browser/headless_web_contents_impl.cc index 2bb690e0..95136ad 100644 --- a/headless/lib/browser/headless_web_contents_impl.cc +++ b/headless/lib/browser/headless_web_contents_impl.cc
@@ -350,8 +350,6 @@ } HeadlessWebContentsImpl::~HeadlessWebContentsImpl() { - for (auto& observer : observers_) - observer.HeadlessWebContentsDestroyed(); agent_host_->RemoveObserver(this); if (render_process_host_) render_process_host_->RemoveObserver(this); @@ -361,23 +359,6 @@ FROM_HERE, std::move(window_tree_host_)); } -void HeadlessWebContentsImpl::RenderFrameCreated( - content::RenderFrameHost* render_frame_host) { - browser_context_->SetDevToolsFrameToken( - render_frame_host->GetProcess()->GetID(), - render_frame_host->GetRoutingID(), - render_frame_host->GetDevToolsFrameToken(), - render_frame_host->GetFrameTreeNodeId()); -} - -void HeadlessWebContentsImpl::RenderFrameDeleted( - content::RenderFrameHost* render_frame_host) { - browser_context_->RemoveDevToolsFrameToken( - render_frame_host->GetProcess()->GetID(), - render_frame_host->GetRoutingID(), - render_frame_host->GetFrameTreeNodeId()); -} - void HeadlessWebContentsImpl::RenderViewReady() { DCHECK(web_contents()->GetPrimaryMainFrame()->IsRenderFrameLive()); @@ -390,27 +371,6 @@ devtools_target_ready_notification_sent_ = true; } -int HeadlessWebContentsImpl::GetMainFrameRenderProcessId() const { - if (!web_contents() || !web_contents()->GetPrimaryMainFrame()) - return -1; - return web_contents()->GetPrimaryMainFrame()->GetProcess()->GetID(); -} - -int HeadlessWebContentsImpl::GetMainFrameTreeNodeId() const { - if (!web_contents() || !web_contents()->GetPrimaryMainFrame()) - return -1; - return web_contents()->GetPrimaryMainFrame()->GetFrameTreeNodeId(); -} - -std::string HeadlessWebContentsImpl::GetMainFrameDevToolsId() const { - if (!web_contents() || !web_contents()->GetPrimaryMainFrame()) - return ""; - return web_contents() - ->GetPrimaryMainFrame() - ->GetDevToolsFrameToken() - .ToString(); -} - bool HeadlessWebContentsImpl::OpenURL(const GURL& url) { if (!url.is_valid()) return false; @@ -440,23 +400,10 @@ observers_.RemoveObserver(observer); } -void HeadlessWebContentsImpl::DevToolsAgentHostAttached( - content::DevToolsAgentHost* agent_host) { - for (auto& observer : observers_) - observer.DevToolsClientAttached(); -} - -void HeadlessWebContentsImpl::DevToolsAgentHostDetached( - content::DevToolsAgentHost* agent_host) { - for (auto& observer : observers_) - observer.DevToolsClientDetached(); -} - void HeadlessWebContentsImpl::RenderProcessExited( content::RenderProcessHost* host, const content::ChildProcessTerminationInfo& info) { DCHECK_EQ(render_process_host_, host); - render_process_exited_ = true; for (auto& observer : observers_) observer.RenderProcessExited(info.status, info.exit_code); }
diff --git a/headless/lib/browser/headless_web_contents_impl.h b/headless/lib/browser/headless_web_contents_impl.h index b80147fd..e14cff8 100644 --- a/headless/lib/browser/headless_web_contents_impl.h +++ b/headless/lib/browser/headless_web_contents_impl.h
@@ -6,7 +6,6 @@ #define HEADLESS_LIB_BROWSER_HEADLESS_WEB_CONTENTS_IMPL_H_ #include <memory> -#include <set> #include <string> #include "base/memory/raw_ptr.h" @@ -65,22 +64,12 @@ void AddObserver(Observer* observer) override; void RemoveObserver(Observer* observer) override; HeadlessDevToolsTarget* GetDevToolsTarget() override; - int GetMainFrameRenderProcessId() const override; - int GetMainFrameTreeNodeId() const override; - std::string GetMainFrameDevToolsId() const override; - std::unique_ptr<HeadlessDevToolsChannel> CreateDevToolsChannel() override; // HeadlessDevToolsTarget implementation: void AttachClient(HeadlessDevToolsClient* client) override; void DetachClient(HeadlessDevToolsClient* client) override; bool IsAttached() override; - // content::DevToolsAgentHostObserver implementation: - void DevToolsAgentHostAttached( - content::DevToolsAgentHost* agent_host) override; - void DevToolsAgentHostDetached( - content::DevToolsAgentHost* agent_host) override; - // content::RenderProcessHostObserver implementation: void RenderProcessExited( content::RenderProcessHost* host, @@ -88,10 +77,9 @@ void RenderProcessHostDestroyed(content::RenderProcessHost* host) override; // content::WebContentsObserver implementation: - void RenderFrameCreated(content::RenderFrameHost* render_frame_host) override; - void RenderFrameDeleted(content::RenderFrameHost* render_frame_host) override; void RenderViewReady() override; + std::unique_ptr<HeadlessDevToolsChannel> CreateDevToolsChannel(); content::WebContents* web_contents() const; bool OpenURL(const GURL& url); @@ -159,7 +147,6 @@ std::unique_ptr<content::WebContents> web_contents_; scoped_refptr<content::DevToolsAgentHost> agent_host_; bool devtools_target_ready_notification_sent_ = false; - bool render_process_exited_ = false; bool use_tab_target_ = false; base::ObserverList<HeadlessWebContents::Observer>::Unchecked observers_;
diff --git a/headless/public/headless_web_contents.h b/headless/public/headless_web_contents.h index 9661329d..a8d3209 100644 --- a/headless/public/headless_web_contents.h +++ b/headless/public/headless_web_contents.h
@@ -5,13 +5,8 @@ #ifndef HEADLESS_PUBLIC_HEADLESS_WEB_CONTENTS_H_ #define HEADLESS_PUBLIC_HEADLESS_WEB_CONTENTS_H_ -#include <string> -#include <utility> - -#include "base/functional/callback.h" #include "base/memory/raw_ptr.h" #include "base/process/kill.h" -#include "headless/public/headless_devtools_channel.h" #include "headless/public/headless_export.h" #include "ui/gfx/geometry/size.h" #include "url/gurl.h" @@ -44,15 +39,6 @@ // // TODO(altimin): Support this event for pages that aren't created by us. virtual void DevToolsTargetReady() {} - - // Indicates that a DevTools client attached to this HeadlessWebContents - // instance. - virtual void DevToolsClientAttached() {} - - // Indicates that a DevTools client detached from this HeadlessWebContents - // instance. - virtual void DevToolsClientDetached() {} - // This method is invoked when the process of the observed RenderProcessHost // exits (either normally or with a crash). To determine if the process // closed normally or crashed, examine the |status| parameter. @@ -63,9 +49,6 @@ virtual void RenderProcessExited(base::TerminationStatus status, int exit_code) {} - // Invoked when HeadlessWebContents is being destroyed. - virtual void HeadlessWebContentsDestroyed() {} - protected: Observer() {} virtual ~Observer() {} @@ -81,23 +64,9 @@ // signaled. virtual HeadlessDevToolsTarget* GetDevToolsTarget() = 0; - // Creates a DevTools channel corresponding to this tab. Note that this method - // won't return a valid value until Observer::DevToolsTargetReady has been - // signaled. - virtual std::unique_ptr<HeadlessDevToolsChannel> CreateDevToolsChannel() = 0; - // Close this page. |HeadlessWebContents| object will be destroyed. virtual void Close() = 0; - // Returns the main frame's process id or -1 if there's no main frame. - virtual int GetMainFrameRenderProcessId() const = 0; - - // Returns the main frame's node id or -1 if there's no main frame. - virtual int GetMainFrameTreeNodeId() const = 0; - - // Returns the main frame's devtools id or "" if there's no main frame. - virtual std::string GetMainFrameDevToolsId() const = 0; - protected: HeadlessWebContents() {} };
diff --git a/headless/test/headless_devtools_client_browsertest.cc b/headless/test/headless_devtools_client_browsertest.cc index 0fb6d87..b1c76379 100644 --- a/headless/test/headless_devtools_client_browsertest.cc +++ b/headless/test/headless_devtools_client_browsertest.cc
@@ -539,29 +539,6 @@ HEADLESS_DEVTOOLED_TEST_F(DevToolsNetworkOfflineEmulationTest); -class DevToolsAttachAndDetachNotifications - : public HeadlessDevTooledBrowserTest { - public: - void DevToolsClientAttached() override { dev_tools_client_attached_ = true; } - - void RunDevTooledTest() override { - EXPECT_TRUE(dev_tools_client_attached_); - FinishAsynchronousTest(); - } - - void DevToolsClientDetached() override { dev_tools_client_detached_ = true; } - - void TearDownOnMainThread() override { - EXPECT_TRUE(dev_tools_client_detached_); - } - - private: - bool dev_tools_client_attached_ = false; - bool dev_tools_client_detached_ = false; -}; - -HEADLESS_DEVTOOLED_TEST_F(DevToolsAttachAndDetachNotifications); - class DomTreeExtractionBrowserTest : public HeadlessDevTooledBrowserTest { public: void RunDevTooledTest() override {
diff --git a/ios/chrome/browser/autofill/form_structure_browsertest.mm b/ios/chrome/browser/autofill/form_structure_browsertest.mm index b8d3ae6..1d79e83 100644 --- a/ios/chrome/browser/autofill/form_structure_browsertest.mm +++ b/ios/chrome/browser/autofill/form_structure_browsertest.mm
@@ -45,6 +45,7 @@ #import "ios/chrome/browser/web/chrome_web_client.h" #import "ios/web/public/js_messaging/web_frame.h" #import "ios/web/public/js_messaging/web_frames_manager.h" +#import "ios/web/public/test/js_test_util.h" #import "ios/web/public/test/scoped_testing_web_client.h" #import "ios/web/public/test/task_observer_util.h" #import "ios/web/public/test/web_state_test_util.h" @@ -278,11 +279,13 @@ return false; } + autofill::FormUtilJavaScriptFeature* feature = + autofill::FormUtilJavaScriptFeature::GetInstance(); + __block web::WebFrame* main_frame = nullptr; success = WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^bool { web::WebFramesManager* frames_manager = - autofill::FormUtilJavaScriptFeature::GetInstance()->GetWebFramesManager( - web_state()); + feature->GetWebFramesManager(web_state()); main_frame = frames_manager->GetMainWebFrame(); return main_frame != nullptr; }); @@ -292,14 +295,13 @@ DCHECK(main_frame); uint32_t next_available_id = 1; - autofill::FormUtilJavaScriptFeature::GetInstance() - ->SetUpForUniqueIDsWithInitialState(main_frame, next_available_id); + feature->SetUpForUniqueIDsWithInitialState(main_frame, next_available_id); // Wait for `SetUpForUniqueIDsWithInitialState` to complete. return WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^bool { - return [web::test::ExecuteJavaScript(@"document[__gCrWeb.fill.ID_SYMBOL]", - web_state()) intValue] == - static_cast<int>(next_available_id); + return [web::test::ExecuteJavaScriptForFeature( + web_state(), @"document[__gCrWeb.fill.ID_SYMBOL]", feature) + intValue] == static_cast<int>(next_available_id); }); }
diff --git a/media/capture/video/chromeos/camera_device_delegate_unittest.cc b/media/capture/video/chromeos/camera_device_delegate_unittest.cc index 645ac06e..8fe631f2 100644 --- a/media/capture/video/chromeos/camera_device_delegate_unittest.cc +++ b/media/capture/video/chromeos/camera_device_delegate_unittest.cc
@@ -116,6 +116,11 @@ MOCK_METHOD2(DoConfigureStreamsAndGetAllocatedBuffers, void(cros::mojom::Camera3StreamConfigurationPtr& config, ConfigureStreamsAndGetAllocatedBuffersCallback& callback)); + + void SignalStreamFlush(const std::vector<uint64_t>& stream_ids) override { + DoSignalStreamFlush(stream_ids); + } + MOCK_METHOD1(DoSignalStreamFlush, void(std::vector<uint64_t> stream_ids)); }; constexpr int32_t kJpegMaxBufferSize = 1024;
diff --git a/media/capture/video/chromeos/mojom/camera3.mojom b/media/capture/video/chromeos/mojom/camera3.mojom index 40b9c4a9..1a81d203 100644 --- a/media/capture/video/chromeos/mojom/camera3.mojom +++ b/media/capture/video/chromeos/mojom/camera3.mojom
@@ -163,6 +163,35 @@ Camera3NotifyMsgMessage message; }; +enum Camera3BufferRequestStatus { + CAMERA3_BUF_REQ_OK = 0, + CAMERA3_BUF_REQ_FAILED_PARTIAL = 1, + CAMERA3_BUF_REQ_FAILED_CONFIGURING = 2, + CAMERA3_BUF_REQ_FAILED_ILLEGAL_ARGUMENTS = 3, + CAMERA3_BUF_REQ_FAILED_UNKNOWN = 4, + CAMERA3_BUF_REQ_NUM_STATUS +}; + +enum Camera3StreamBufferReqStatus { + CAMERA3_PS_BUF_REQ_OK = 0, + CAMERA3_PS_BUF_REQ_NO_BUFFER_AVAILABLE = 1, + CAMERA3_PS_BUF_REQ_MAX_BUFFER_EXCEEDED = 2, + CAMERA3_PS_BUF_REQ_STREAM_DISCONNECTED = 3, + CAMERA3_PS_BUF_REQ_UNKNOWN_ERROR = 4, + CAMERA3_PS_BUF_REQ_NUM_STATUS +}; + +struct Camera3BufferRequest { + uint64 stream_id; + uint32 num_buffers_requested; +}; + +struct Camera3StreamBufferRet { + uint64 stream_id; + Camera3StreamBufferReqStatus status; + array<Camera3StreamBuffer>? output_buffers; +}; + enum Camera3RequestTemplate { CAMERA3_TEMPLATE_PREVIEW = 1, CAMERA3_TEMPLATE_STILL_CAPTURE = 2, @@ -204,7 +233,7 @@ // in Android camera HAL v3. For the work flow of the functions in // Camera3CallbackOps, see the comments about Camera3DeviceOps above. // -// Next method ID: 2 +// Next method ID: 4 interface Camera3CallbackOps { // ProcessCaptureResult() is called by the camera HAL to send result metadata // and filled buffer to the client. @@ -213,6 +242,18 @@ // Notify() is called by the camera HAL to notify the client of the start of // each capture, and of errors encountered. Notify@1(Camera3NotifyMsg msg); + + // RequestStreamBuffers() is called by the camera HAL to request output + // buffers from the client. + [MinVersion=5] + RequestStreamBuffers@2(array<Camera3BufferRequest> buffer_reqs) => + (Camera3BufferRequestStatus result, + array<Camera3StreamBufferRet>? returned_buf_reqs); + + // ReturnStreamBuffers() is called by the camera HAL to return output buffers + // to the client. + [MinVersion=5] + ReturnStreamBuffers@3(array<Camera3StreamBuffer> buffers); }; // Camera3DeviceOps is mostly a translation of the camera3_device_ops_t API from @@ -269,7 +310,7 @@ // // 7. Close() closes the camera device. // -// Next method ID: 9 +// Next method ID: 10 interface Camera3DeviceOps { // Initialize() is called once after the camera device is opened to register // the Camera3CallbackOps handle. @@ -332,4 +373,11 @@ ConfigureStreamsAndGetAllocatedBuffers@8(Camera3StreamConfiguration config) => (int32 result, Camera3StreamConfiguration? updated_config, map<uint64, array<Camera3StreamBuffer>> allocated_buffers); + + // SignalStreamFlush() can be called when the client is about to call + // ConfigureStreams(). The camera HAL must finish in-flight requests normally + // and return all buffers belonging to the designated streams through + // ProcessCaptureResult() or ReturnStreamBuffers(). + [MinVersion=5] + SignalStreamFlush@9(array<uint64> stream_ids); };
diff --git a/media/capture/video/chromeos/request_manager.cc b/media/capture/video/chromeos/request_manager.cc index 28e340f..d5576d8 100644 --- a/media/capture/video/chromeos/request_manager.cc +++ b/media/capture/video/chromeos/request_manager.cc
@@ -885,6 +885,17 @@ } } +void RequestManager::RequestStreamBuffers( + std::vector<cros::mojom::Camera3BufferRequestPtr> buffer_reqs, + RequestStreamBuffersCallback callback) { + // TODO(b/226688669): Implement RequestManager::RequestStreamBuffers. +} + +void RequestManager::ReturnStreamBuffers( + std::vector<cros::mojom::Camera3StreamBufferPtr> buffers) { + // TODO(b/226688669): Implement RequestManager::ReturnStreamBuffers. +} + void RequestManager::SubmitCaptureResult( uint32_t frame_number, StreamType stream_type,
diff --git a/media/capture/video/chromeos/request_manager.h b/media/capture/video/chromeos/request_manager.h index 34c0ec02..b5269c1 100644 --- a/media/capture/video/chromeos/request_manager.h +++ b/media/capture/video/chromeos/request_manager.h
@@ -263,6 +263,16 @@ StreamType stream_type, cros::mojom::Camera3ErrorMsgCode error_code); + // RequestStreamBuffers receives output buffer requests and a callback to + // receive results. + void RequestStreamBuffers( + std::vector<cros::mojom::Camera3BufferRequestPtr> buffer_reqs, + RequestStreamBuffersCallback callback) override; + + // ReturnStreamBuffers receives returned output buffers. + void ReturnStreamBuffers( + std::vector<cros::mojom::Camera3StreamBufferPtr> buffers) override; + // Submits the captured buffer of frame |frame_number_| for the given // |stream_type| to Chrome if all the required metadata and the captured // buffer are received. After the buffer is submitted the function then
diff --git a/media/gpu/vaapi/vaapi_video_encode_accelerator.cc b/media/gpu/vaapi/vaapi_video_encode_accelerator.cc index a987f6e2..33035b5 100644 --- a/media/gpu/vaapi/vaapi_video_encode_accelerator.cc +++ b/media/gpu/vaapi/vaapi_video_encode_accelerator.cc
@@ -104,8 +104,6 @@ VaapiVideoEncodeAccelerator::VaapiVideoEncodeAccelerator() : can_use_encoder_(num_instances_.Increment() < kMaxNumOfInstances), - output_buffer_byte_size_(0), - state_(kUninitialized), child_task_runner_(base::SequencedTaskRunner::GetCurrentDefault()), // TODO(akahuang): Change to use SequencedTaskRunner to see if the // performance is affected. @@ -394,7 +392,8 @@ const size_t max_ref_frames = encoder_->GetMaxNumOfRefFrames(); num_frames_in_flight_ = std::max(kMinNumFramesInFlight, max_ref_frames); DVLOGF(1) << "Frames in flight: " << num_frames_in_flight_; - + max_pending_results_size_ = + num_frames_in_flight_ * std::max<size_t>(1, config.spatial_layers.size()); if (!vaapi_wrapper_->CreateContext(encoder_->GetCodedSize())) { NOTIFY_ERROR(kPlatformFailureError, "Failed creating VAContext"); return; @@ -437,17 +436,6 @@ DVLOGF(4) << "va_surface_id: " << va_surface_id; va_surfaces->push_back(std::move(va_surface)); - - // At least one surface must available in |available_encode_surfaces_| - // to succeed in EncodePendingInputs(). Checks here to avoid redundant - // EncodePendingInputs() call. - for (const auto& surfaces : available_encode_surfaces_) { - if (surfaces.second.empty()) - return; - } - - if (!input_queue_.empty()) - EncodePendingInputs(); } void VaapiVideoEncodeAccelerator::TryToReturnBitstreamBuffers() { @@ -735,10 +723,9 @@ const VASurfaceID id = scoped_va_surface->id(); const gfx::Size& size = scoped_va_surface->size(); const unsigned int format = scoped_va_surface->format(); - VASurface::ReleaseCB release_cb = - base::BindPostTaskToCurrentDefault(base::BindOnce( - &VaapiVideoEncodeAccelerator::RecycleVASurface, encoder_weak_this_, - &surfaces, std::move(scoped_va_surface))); + VASurface::ReleaseCB release_cb = base::BindOnce( + &VaapiVideoEncodeAccelerator::RecycleVASurface, encoder_weak_this_, + &surfaces, std::move(scoped_va_surface)); return base::MakeRefCounted<VASurface>(id, size, format, std::move(release_cb)); @@ -857,7 +844,15 @@ TRACE_EVENT1("media,gpu", "VAVEA::EncodePendingInputs", "pending input frames", input_queue_.size()); - while (state_ == kEncoding && !input_queue_.empty()) { + // Encode all the frames in |input_queue_|. So that we avoid a number of + // encoded chunks are stuck at |pending_encode_results_|, we breaks if the + // queue size is more than |max_pending_encode_results_size|. Since the + // pending frames to be encoded are held in |input_queue_|, a client that + // recycles the VideoFrames will not input any more frames until an available + // bitstream buffer is given and a pending frame is released thanks to + // the resumed encode. + while (state_ == kEncoding && !input_queue_.empty() && + pending_encode_results_.size() < max_pending_results_size_) { const InputFrameRef& input_frame = input_queue_.front(); if (!input_frame.frame) { // If this is a flush (null) frame, don't create/submit a new encode @@ -963,6 +958,11 @@ available_bitstream_buffers_.push(std::move(buffer)); TryToReturnBitstreamBuffers(); + // If there is a pending frame, it is pended because of the bitstream buffer + // shortage. Try to encode it. + if (!input_queue_.empty()) { + EncodePendingInputs(); + } } void VaapiVideoEncodeAccelerator::RequestEncodingParametersChange( @@ -1064,8 +1064,6 @@ if (vaapi_wrapper_) vaapi_wrapper_->DestroyContext(); - available_encode_surfaces_.clear(); - if (vpp_vaapi_wrapper_) vpp_vaapi_wrapper_->DestroyContext(); @@ -1082,6 +1080,11 @@ encoder_.reset(); + // Clear |available_encode_surfaces_| after |encoder_| is destroyed because + // the reconstructed surface in the reference frame pool owned by |encoder_| + // are back to |available_encode_surfaces_|. + available_encode_surfaces_.clear(); + delete this; }
diff --git a/media/gpu/vaapi/vaapi_video_encode_accelerator.h b/media/gpu/vaapi/vaapi_video_encode_accelerator.h index 496933d7..c4b8cfca 100644 --- a/media/gpu/vaapi/vaapi_video_encode_accelerator.h +++ b/media/gpu/vaapi/vaapi_video_encode_accelerator.h
@@ -245,7 +245,9 @@ gfx::Rect visible_rect_; // Size in bytes required for output bitstream buffers. - size_t output_buffer_byte_size_; + size_t output_buffer_byte_size_ = 0; + // Size of the max size of |pending_encode_results_|. + size_t max_pending_results_size_ = 0; // This flag signals when the client is sending NV12 + DmaBuf-backed // VideoFrames to encode, which allows for skipping a copy-adaptation on @@ -253,13 +255,13 @@ bool native_input_mode_ = false; // The number of frames that needs to be held on encoding. - size_t num_frames_in_flight_; + size_t num_frames_in_flight_ = 0; // All of the members below must be accessed on the encoder_task_runner_, // while it is running. // Encoder state. Encode tasks will only run in kEncoding state. - State state_; + State state_ = State::kUninitialized; // Encoder instance managing video codec state and preparing encode jobs. // Should only be used on |encoder_task_runner_|.
diff --git a/mojo/core/broker_host.cc b/mojo/core/broker_host.cc index a0371495..7a5d4f94 100644 --- a/mojo/core/broker_host.cc +++ b/mojo/core/broker_host.cc
@@ -32,11 +32,8 @@ client_process_(std::move(client_process)) #endif { - CHECK(connection_params.endpoint().is_valid() || - connection_params.server_endpoint().is_valid()); - base::CurrentThread::Get()->AddDestructionObserver(this); - + CHECK(connection_params.endpoint().is_valid()); channel_ = Channel::Create(this, std::move(connection_params), #if BUILDFLAG(IS_WIN) client_process_
diff --git a/mojo/core/channel.cc b/mojo/core/channel.cc index 71d2ab3..4b7bbc1 100644 --- a/mojo/core/channel.cc +++ b/mojo/core/channel.cc
@@ -893,23 +893,13 @@ // static scoped_refptr<Channel> Channel::CreateForIpczDriver( Delegate* delegate, - Endpoint endpoint, + PlatformChannelEndpoint endpoint, scoped_refptr<base::SingleThreadTaskRunner> io_task_runner) { #if BUILDFLAG(IS_NACL) return nullptr; #else - ConnectionParams params = - absl::visit(base::Overloaded{ - [](PlatformChannelEndpoint& endpoint) { - return ConnectionParams(std::move(endpoint)); - }, - [](PlatformChannelServerEndpoint& endpoint) { - return ConnectionParams(std::move(endpoint)); - }, - }, - endpoint); - return Create(delegate, std::move(params), HandlePolicy::kAcceptHandles, - std::move(io_task_runner)); + return Create(delegate, ConnectionParams{std::move(endpoint)}, + HandlePolicy::kAcceptHandles, std::move(io_task_runner)); #endif }
diff --git a/mojo/core/channel.h b/mojo/core/channel.h index 4cc0e92..40e03ebe 100644 --- a/mojo/core/channel.h +++ b/mojo/core/channel.h
@@ -22,9 +22,7 @@ #include "mojo/core/connection_params.h" #include "mojo/core/platform_handle_in_transit.h" #include "mojo/public/cpp/platform/platform_channel_endpoint.h" -#include "mojo/public/cpp/platform/platform_channel_server_endpoint.h" #include "mojo/public/cpp/platform/platform_handle.h" -#include "third_party/abseil-cpp/absl/types/variant.h" namespace mojo::core { @@ -318,11 +316,9 @@ // header, and the Channel is no longer responsible for encoding or decoding // any metadata about transmitted PlatformHandles, since the ipcz driver takes // care of that. - using Endpoint = - absl::variant<PlatformChannelEndpoint, PlatformChannelServerEndpoint>; static scoped_refptr<Channel> CreateForIpczDriver( Delegate* delegate, - Endpoint endpoint, + PlatformChannelEndpoint endpoint, scoped_refptr<base::SingleThreadTaskRunner> io_task_runner); Channel(const Channel&) = delete;
diff --git a/mojo/core/channel_mac.cc b/mojo/core/channel_mac.cc index 24405df2..229ff07 100644 --- a/mojo/core/channel_mac.cc +++ b/mojo/core/channel_mac.cc
@@ -55,14 +55,8 @@ self_(this), io_task_runner_(io_task_runner), watch_controller_(FROM_HERE) { - PlatformHandle channel_handle; - if (connection_params.server_endpoint().is_valid()) { - channel_handle = - connection_params.TakeServerEndpoint().TakePlatformHandle(); - } else { - channel_handle = connection_params.TakeEndpoint().TakePlatformHandle(); - } - + PlatformHandle channel_handle = + connection_params.TakeEndpoint().TakePlatformHandle(); if (channel_handle.is_mach_send()) { send_port_ = channel_handle.TakeMachSendRight(); } else if (channel_handle.is_mach_receive()) {
diff --git a/mojo/core/channel_posix.cc b/mojo/core/channel_posix.cc index f57c9b3..42b55c2 100644 --- a/mojo/core/channel_posix.cc +++ b/mojo/core/channel_posix.cc
@@ -122,12 +122,8 @@ : Channel(delegate, handle_policy), self_(this), io_task_runner_(io_task_runner) { - if (connection_params.server_endpoint().is_valid()) - server_ = connection_params.TakeServerEndpoint(); - else - socket_ = connection_params.TakeEndpoint().TakePlatformHandle().TakeFD(); - - CHECK(server_.is_valid() || socket_.is_valid()); + socket_ = connection_params.TakeEndpoint().TakePlatformHandle().TakeFD(); + CHECK(socket_.is_valid()); } ChannelPosix::~ChannelPosix() { @@ -225,19 +221,13 @@ read_watcher_ = std::make_unique<base::MessagePumpForIO::FdWatchController>(FROM_HERE); base::CurrentThread::Get()->AddDestructionObserver(this); - if (server_.is_valid()) { - base::CurrentIOThread::Get()->WatchFileDescriptor( - server_.platform_handle().GetFD().get(), false /* persistent */, - base::MessagePumpForIO::WATCH_READ, read_watcher_.get(), this); - } else { - write_watcher_ = - std::make_unique<base::MessagePumpForIO::FdWatchController>(FROM_HERE); - base::CurrentIOThread::Get()->WatchFileDescriptor( - socket_.get(), true /* persistent */, - base::MessagePumpForIO::WATCH_READ, read_watcher_.get(), this); - base::AutoLock lock(write_lock_); - FlushOutgoingMessagesNoLock(); - } + write_watcher_ = + std::make_unique<base::MessagePumpForIO::FdWatchController>(FROM_HERE); + base::CurrentIOThread::Get()->WatchFileDescriptor( + socket_.get(), true /* persistent */, base::MessagePumpForIO::WATCH_READ, + read_watcher_.get(), this); + base::AutoLock lock(write_lock_); + FlushOutgoingMessagesNoLock(); } void ChannelPosix::WaitForWriteOnIOThread() { @@ -268,10 +258,8 @@ write_watcher_.reset(); if (leak_handle_) { std::ignore = socket_.release(); - server_.TakePlatformHandle().release(); } else { socket_.reset(); - std::ignore = server_.TakePlatformHandle(); } #if BUILDFLAG(IS_IOS) fds_to_close_.clear(); @@ -288,24 +276,6 @@ } void ChannelPosix::OnFileCanReadWithoutBlocking(int fd) { - if (server_.is_valid()) { - CHECK_EQ(fd, server_.platform_handle().GetFD().get()); -#if !BUILDFLAG(IS_NACL) - read_watcher_.reset(); - base::CurrentThread::Get()->RemoveDestructionObserver(this); - - AcceptSocketConnection(server_.platform_handle().GetFD().get(), &socket_); - std::ignore = server_.TakePlatformHandle(); - if (!socket_.is_valid()) { - OnError(Error::kConnectionFailed); - return; - } - StartOnIOThread(); -#else - NOTREACHED(); -#endif - return; - } CHECK_EQ(fd, socket_.get()); bool validation_error = false; @@ -370,10 +340,6 @@ // cannot be written, it's queued and a wait is initiated to write the message // ASAP on the I/O thread. bool ChannelPosix::WriteNoLock(MessageView message_view) { - if (server_.is_valid()) { - outgoing_messages_.emplace_front(std::move(message_view)); - return true; - } size_t bytes_written = 0; std::vector<PlatformHandleInTransit> handles = message_view.TakeHandles(); size_t num_handles = handles.size();
diff --git a/mojo/core/channel_posix.h b/mojo/core/channel_posix.h index 695be6d..94b55c9 100644 --- a/mojo/core/channel_posix.h +++ b/mojo/core/channel_posix.h
@@ -98,12 +98,7 @@ bool CloseHandles(const int* fds, size_t num_fds); #endif // BUILDFLAG(IS_IOS) - // We may be initialized with a server socket, in which case this will be - // valid until it accepts an incoming connection. - PlatformChannelServerEndpoint server_; - - // The socket over which to communicate. May be passed in at construction time - // or accepted over |server_|. + // The socket over which to communicate. base::ScopedFD socket_; // These watchers must only be accessed on the IO thread.
diff --git a/mojo/core/channel_win.cc b/mojo/core/channel_win.cc index f75273f..9f751fa9 100644 --- a/mojo/core/channel_win.cc +++ b/mojo/core/channel_win.cc
@@ -67,16 +67,8 @@ is_untrusted_process_(connection_params.is_untrusted_process()), self_(this), io_task_runner_(io_task_runner) { - if (connection_params.server_endpoint().is_valid()) { - handle_ = connection_params.TakeServerEndpoint() - .TakePlatformHandle() - .TakeHandle(); - needs_connection_ = true; - } else { - handle_ = - connection_params.TakeEndpoint().TakePlatformHandle().TakeHandle(); - } - + handle_ = + connection_params.TakeEndpoint().TakePlatformHandle().TakeHandle(); CHECK(handle_.IsValid()); } @@ -186,29 +178,6 @@ base::CurrentThread::Get()->AddDestructionObserver(this); base::CurrentIOThread::Get()->RegisterIOHandler(handle_.Get(), this); - if (needs_connection_) { - BOOL ok = ::ConnectNamedPipe(handle_.Get(), &connect_context_.overlapped); - if (ok) { - PLOG(ERROR) << "Unexpected success while waiting for pipe connection"; - OnError(Error::kConnectionFailed); - return; - } - - const DWORD err = GetLastError(); - switch (err) { - case ERROR_PIPE_CONNECTED: - break; - case ERROR_IO_PENDING: - is_connect_pending_ = true; - AddRef(); - return; - case ERROR_NO_DATA: - default: - OnError(Error::kConnectionFailed); - return; - } - } - // Now that we have registered our IOHandler, we can start writing. { base::AutoLock lock(write_lock_); @@ -261,16 +230,6 @@ } else { OnError(Error::kDisconnected); } - } else if (context == &connect_context_) { - DCHECK(is_connect_pending_); - is_connect_pending_ = false; - ReadMore(0); - - base::AutoLock lock(write_lock_); - if (delay_writes_) { - delay_writes_ = false; - WriteNextNoLock(); - } } else if (context == &read_context_) { OnReadDone(static_cast<size_t>(bytes_transfered)); } else { @@ -381,8 +340,9 @@ // If we can't write because the pipe is disconnected then continue // reading to fetch any in-flight messages, relying on end-of-stream to // signal the actual disconnection. - if (is_read_pending_ || is_connect_pending_) + if (is_read_pending_) { return; + } } OnError(error); @@ -396,14 +356,9 @@ // The pipe handle this Channel uses for communication. base::win::ScopedHandle handle_; - // Indicates whether |handle_| must wait for a connection. - bool needs_connection_ = false; - const scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_; - base::MessagePumpForIO::IOContext connect_context_; base::MessagePumpForIO::IOContext read_context_; - bool is_connect_pending_ = false; bool is_read_pending_ = false; // Protects all fields potentially accessed on multiple threads via Write().
diff --git a/mojo/core/connection_params.cc b/mojo/core/connection_params.cc index dac76f1d..b05b211 100644 --- a/mojo/core/connection_params.cc +++ b/mojo/core/connection_params.cc
@@ -15,10 +15,6 @@ ConnectionParams::ConnectionParams(PlatformChannelEndpoint endpoint) : endpoint_(std::move(endpoint)) {} -ConnectionParams::ConnectionParams( - PlatformChannelServerEndpoint server_endpoint) - : server_endpoint_(std::move(server_endpoint)) {} - ConnectionParams::ConnectionParams(ConnectionParams&&) = default; ConnectionParams::~ConnectionParams() = default;
diff --git a/mojo/core/connection_params.h b/mojo/core/connection_params.h index 052f38b..bf8dce5 100644 --- a/mojo/core/connection_params.h +++ b/mojo/core/connection_params.h
@@ -8,7 +8,6 @@ #include "build/build_config.h" #include "mojo/core/system_impl_export.h" #include "mojo/public/cpp/platform/platform_channel_endpoint.h" -#include "mojo/public/cpp/platform/platform_channel_server_endpoint.h" namespace mojo { namespace core { @@ -18,7 +17,6 @@ public: ConnectionParams(); explicit ConnectionParams(PlatformChannelEndpoint endpoint); - explicit ConnectionParams(PlatformChannelServerEndpoint server_endpoint); ConnectionParams(ConnectionParams&&); ConnectionParams(const ConnectionParams&) = delete; @@ -29,16 +27,9 @@ ConnectionParams& operator=(ConnectionParams&&); const PlatformChannelEndpoint& endpoint() const { return endpoint_; } - const PlatformChannelServerEndpoint& server_endpoint() const { - return server_endpoint_; - } PlatformChannelEndpoint TakeEndpoint() { return std::move(endpoint_); } - PlatformChannelServerEndpoint TakeServerEndpoint() { - return std::move(server_endpoint_); - } - void set_is_async(bool is_async) { is_async_ = is_async; } bool is_async() const { return is_async_; } @@ -55,7 +46,6 @@ bool is_untrusted_process_ = false; bool leak_endpoint_ = false; PlatformChannelEndpoint endpoint_; - PlatformChannelServerEndpoint server_endpoint_; }; } // namespace core
diff --git a/mojo/core/core.cc b/mojo/core/core.cc index 415c175..40027e0 100644 --- a/mojo/core/core.cc +++ b/mojo/core/core.cc
@@ -1301,8 +1301,6 @@ return MOJO_RESULT_INVALID_ARGUMENT; if (transport_endpoint->type != MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL && transport_endpoint->type != - MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL_SERVER && - transport_endpoint->type != MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL_ASYNC) { return MOJO_RESULT_UNIMPLEMENTED; } @@ -1318,18 +1316,8 @@ if (!endpoint.is_valid()) return MOJO_RESULT_INVALID_ARGUMENT; - ConnectionParams connection_params; -#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_POSIX) - if (transport_endpoint->type == - MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL_SERVER) { - connection_params = - ConnectionParams(PlatformChannelServerEndpoint(std::move(endpoint))); - } -#endif - if (!connection_params.server_endpoint().is_valid()) { - connection_params = - ConnectionParams(PlatformChannelEndpoint(std::move(endpoint))); - } + ConnectionParams connection_params( + PlatformChannelEndpoint(std::move(endpoint))); // At this point everything else has been validated, so we can take ownership // of the dispatcher. @@ -1342,7 +1330,6 @@ // Release ownership of the endpoint platform handle, per the API // contract. The caller retains ownership on failure. connection_params.TakeEndpoint().TakePlatformHandle().release(); - connection_params.TakeServerEndpoint().TakePlatformHandle().release(); return result; } DCHECK_EQ(removed_dispatcher.get(), invitation_dispatcher); @@ -1400,8 +1387,6 @@ return MOJO_RESULT_INVALID_ARGUMENT; if (transport_endpoint->type != MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL && transport_endpoint->type != - MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL_SERVER && - transport_endpoint->type != MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL_ASYNC) { return MOJO_RESULT_UNIMPLEMENTED; } @@ -1421,18 +1406,8 @@ return MOJO_RESULT_INVALID_ARGUMENT; } - ConnectionParams connection_params; -#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_POSIX) - if (transport_endpoint->type == - MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL_SERVER) { - connection_params = - ConnectionParams(PlatformChannelServerEndpoint(std::move(endpoint))); - } -#endif - if (!connection_params.server_endpoint().is_valid()) { - connection_params = - ConnectionParams(PlatformChannelEndpoint(std::move(endpoint))); - } + ConnectionParams connection_params( + PlatformChannelEndpoint(std::move(endpoint))); if (options && options->flags & MOJO_ACCEPT_INVITATION_FLAG_LEAK_TRANSPORT_ENDPOINT) { connection_params.set_leak_endpoint(true);
diff --git a/mojo/core/embedder/BUILD.gn b/mojo/core/embedder/BUILD.gn index fce6597c..df6f3e93 100644 --- a/mojo/core/embedder/BUILD.gn +++ b/mojo/core/embedder/BUILD.gn
@@ -21,13 +21,13 @@ public_deps = [ "//base", "//mojo:buildflags", + "//mojo/core:embedder_internal", "//mojo/public/cpp/platform", "//third_party/ipcz/src:ipcz_chromium", ] deps = [ ":features", - "//mojo/core:embedder_internal", "//mojo/public/c/system", ] }
diff --git a/mojo/core/invitation_unittest.cc b/mojo/core/invitation_unittest.cc index 346e290..f8c11dd 100644 --- a/mojo/core/invitation_unittest.cc +++ b/mojo/core/invitation_unittest.cc
@@ -45,11 +45,6 @@ namespace core { namespace { -enum class TransportType { - kChannel, - kChannelServer, -}; - const char kSecondaryChannelHandleSwitch[] = "test-secondary-channel-handle"; class InvitationTest : public test::MojoTestBase { @@ -66,7 +61,6 @@ const std::string& test_client_name, MojoHandle* primordial_pipes, size_t num_primordial_pipes, - TransportType transport_type, MojoSendInvitationFlags send_flags, MojoProcessErrorHandler error_handler = nullptr, uintptr_t error_handler_context = 0, @@ -78,7 +72,6 @@ base::ProcessHandle process, MojoHandle* primordial_pipes, size_t num_primordial_pipes, - TransportType transport_type, MojoSendInvitationFlags flags, MojoProcessErrorHandler error_handler, uintptr_t error_handler_context, @@ -106,8 +99,9 @@ #error "Platform not yet supported." #endif - if (switch_name.empty()) + if (switch_name.empty()) { switch_name = PlatformChannel::kHandleSwitch; + } command_line->AppendSwitchASCII(std::string(switch_name), value); } @@ -305,7 +299,6 @@ const std::string& test_client_name, MojoHandle* primordial_pipes, size_t num_primordial_pipes, - TransportType transport_type, MojoSendInvitationFlags send_flags, MojoProcessErrorHandler error_handler, uintptr_t error_handler_context, @@ -323,31 +316,10 @@ launch_options.start_hidden = true; #endif -#if !BUILDFLAG(IS_FUCHSIA) - absl::optional<NamedPlatformChannel> named_channel; -#endif - absl::optional<PlatformChannel> channel; + PlatformChannel channel; PlatformHandle local_endpoint_handle; - if (transport_type == TransportType::kChannel) { - channel.emplace(); - PrepareToPassRemoteEndpoint(&channel.value(), &launch_options, - &command_line); - local_endpoint_handle = channel->TakeLocalEndpoint().TakePlatformHandle(); - } else { -#if !BUILDFLAG(IS_FUCHSIA) - NamedPlatformChannel::Options named_channel_options; -#if !BUILDFLAG(IS_WIN) - CHECK(base::PathService::Get(base::DIR_TEMP, - &named_channel_options.socket_dir)); -#endif - named_channel.emplace(named_channel_options); - named_channel->PassServerNameOnCommandLine(&command_line); - local_endpoint_handle = - named_channel->TakeServerEndpoint().TakePlatformHandle(); -#else // !BUILDFLAG(IS_FUCHSIA) - NOTREACHED() << "Named pipe support does not exist for Mojo on Fuchsia."; -#endif // !BUILDFLAG(IS_FUCHSIA) - } + PrepareToPassRemoteEndpoint(&channel, &launch_options, &command_line); + local_endpoint_handle = channel.TakeLocalEndpoint().TakePlatformHandle(); std::string enable_features; std::string disable_features; @@ -362,13 +334,12 @@ base::Process child_process = base::SpawnMultiProcessTestChild( test_client_name, command_line, launch_options); - if (channel) - channel->RemoteProcessLaunchAttempted(); + channel.RemoteProcessLaunchAttempted(); SendInvitationToClient(std::move(local_endpoint_handle), child_process.Handle(), primordial_pipes, - num_primordial_pipes, transport_type, send_flags, - error_handler, error_handler_context, ""); + num_primordial_pipes, send_flags, error_handler, + error_handler_context, ""); return child_process; } @@ -379,7 +350,6 @@ base::ProcessHandle process, MojoHandle* primordial_pipes, size_t num_primordial_pipes, - TransportType transport_type, MojoSendInvitationFlags flags, MojoProcessErrorHandler error_handler, uintptr_t error_handler_context, @@ -407,10 +377,7 @@ MojoInvitationTransportEndpoint transport_endpoint; transport_endpoint.struct_size = sizeof(transport_endpoint); - if (transport_type == TransportType::kChannel) - transport_endpoint.type = MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL; - else - transport_endpoint.type = MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL_SERVER; + transport_endpoint.type = MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL; transport_endpoint.num_platform_handles = 1; transport_endpoint.platform_handles = &handle; @@ -436,17 +403,12 @@ base::StringPiece switch_name = {}) { const auto& command_line = *base::CommandLine::ForCurrentProcess(); PlatformChannelEndpoint channel_endpoint; -#if !BUILDFLAG(IS_FUCHSIA) - channel_endpoint = NamedPlatformChannel::ConnectToServer(command_line); -#endif - if (!channel_endpoint.is_valid()) { - if (switch_name.empty()) { - channel_endpoint = - PlatformChannel::RecoverPassedEndpointFromCommandLine(command_line); - } else { - channel_endpoint = PlatformChannel::RecoverPassedEndpointFromString( - command_line.GetSwitchValueASCII(switch_name)); - } + if (switch_name.empty()) { + channel_endpoint = + PlatformChannel::RecoverPassedEndpointFromCommandLine(command_line); + } else { + channel_endpoint = PlatformChannel::RecoverPassedEndpointFromString( + command_line.GetSwitchValueASCII(switch_name)); } MojoPlatformHandle endpoint_handle; PlatformHandle::ToMojoPlatformHandle(channel_endpoint.TakePlatformHandle(), @@ -487,9 +449,9 @@ TEST_F(InvitationTest, SendInvitation) { MojoHandle primordial_pipe; - base::Process child_process = LaunchChildTestClient( - "SendInvitationClient", &primordial_pipe, 1, TransportType::kChannel, - MOJO_SEND_INVITATION_FLAG_NONE); + base::Process child_process = + LaunchChildTestClient("SendInvitationClient", &primordial_pipe, 1, + MOJO_SEND_INVITATION_FLAG_NONE); WriteMessage(primordial_pipe, kTestMessage1); EXPECT_EQ(MOJO_RESULT_OK, @@ -523,9 +485,9 @@ TEST_F(InvitationTest, SendInvitationMultiplePipes) { MojoHandle pipes[2]; - base::Process child_process = LaunchChildTestClient( - "SendInvitationMultiplePipesClient", pipes, 2, TransportType::kChannel, - MOJO_SEND_INVITATION_FLAG_NONE); + base::Process child_process = + LaunchChildTestClient("SendInvitationMultiplePipesClient", pipes, 2, + MOJO_SEND_INVITATION_FLAG_NONE); WriteMessage(pipes[0], kTestMessage1); WriteMessage(pipes[1], kTestMessage2); @@ -570,51 +532,6 @@ EXPECT_EQ(MOJO_RESULT_OK, MojoClose(pipes[1])); } -// Fuchsia has no named pipe support. -#if !BUILDFLAG(IS_FUCHSIA) -// TODO(crbug.com/1426421): Flaky on Linux TSAN. -#if BUILDFLAG(IS_LINUX) && defined(THREAD_SANITIZER) -#define MAYBE_SendInvitationWithServer DISABLED_SendInvitationWithServer -#else -#define MAYBE_SendInvitationWithServer SendInvitationWithServer -#endif -TEST_F(InvitationTest, MAYBE_SendInvitationWithServer) { - MojoHandle primordial_pipe; - base::Process child_process = LaunchChildTestClient( - "SendInvitationWithServerClient", &primordial_pipe, 1, - TransportType::kChannelServer, MOJO_SEND_INVITATION_FLAG_NONE); - - WriteMessage(primordial_pipe, kTestMessage1); - EXPECT_EQ(MOJO_RESULT_OK, - WaitForSignals(primordial_pipe, MOJO_HANDLE_SIGNAL_READABLE)); - EXPECT_EQ(kTestMessage3, ReadMessage(primordial_pipe)); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(primordial_pipe)); - - int wait_result = -1; - base::WaitForMultiprocessTestChildExit( - child_process, TestTimeouts::action_timeout(), &wait_result); - child_process.Close(); - EXPECT_EQ(0, wait_result); -} - -DEFINE_TEST_CLIENT(SendInvitationWithServerClient) { - MojoHandle primordial_pipe; - MojoHandle invitation = AcceptInvitation(MOJO_ACCEPT_INVITATION_FLAG_NONE); - const uint32_t pipe_name = 0; - ASSERT_EQ(MOJO_RESULT_OK, - MojoExtractMessagePipeFromInvitation(invitation, &pipe_name, 4, - nullptr, &primordial_pipe)); - ASSERT_EQ(MOJO_RESULT_OK, MojoClose(invitation)); - - WaitForSignals(primordial_pipe, MOJO_HANDLE_SIGNAL_READABLE); - ASSERT_EQ(kTestMessage1, ReadMessage(primordial_pipe)); - WriteMessage(primordial_pipe, kTestMessage3); - WaitForSignals(primordial_pipe, MOJO_HANDLE_SIGNAL_PEER_CLOSED); - - ASSERT_EQ(MOJO_RESULT_OK, MojoClose(primordial_pipe)); -} -#endif // !BUILDFLAG(IS_FUCHSIA) - const char kErrorMessage[] = "ur bad :("; const char kDisconnectMessage[] = "go away plz"; @@ -677,9 +594,8 @@ RemoteProcessState process_state; MojoHandle pipe; base::Process child_process = LaunchChildTestClient( - "ProcessErrorsClient", &pipe, 1, TransportType::kChannel, - MOJO_SEND_INVITATION_FLAG_NONE, &TestProcessErrorHandler, - reinterpret_cast<uintptr_t>(&process_state)); + "ProcessErrorsClient", &pipe, 1, MOJO_SEND_INVITATION_FLAG_NONE, + &TestProcessErrorHandler, reinterpret_cast<uintptr_t>(&process_state)); MojoMessageHandle message; WaitForSignals(pipe, MOJO_HANDLE_SIGNAL_READABLE); @@ -750,9 +666,8 @@ MojoHandle pipe; base::Process child_process = LaunchChildTestClient( - "ReinvitationClient", &pipe, 1, TransportType::kChannel, - MOJO_SEND_INVITATION_FLAG_NONE, nullptr, 0, &command_line, - &launch_options); + "ReinvitationClient", &pipe, 1, MOJO_SEND_INVITATION_FLAG_NONE, nullptr, + 0, &command_line, &launch_options); secondary_channel.RemoteProcessLaunchAttempted(); // Synchronize end-to-end communication first to ensure the process connection @@ -819,9 +734,9 @@ TEST_F(InvitationTest, SendIsolatedInvitation) { MojoHandle primordial_pipe; - base::Process child_process = LaunchChildTestClient( - "SendIsolatedInvitationClient", &primordial_pipe, 1, - TransportType::kChannel, MOJO_SEND_INVITATION_FLAG_ISOLATED); + base::Process child_process = + LaunchChildTestClient("SendIsolatedInvitationClient", &primordial_pipe, 1, + MOJO_SEND_INVITATION_FLAG_ISOLATED); WriteMessage(primordial_pipe, kTestMessage1); EXPECT_EQ(MOJO_RESULT_OK, @@ -874,8 +789,7 @@ MojoHandle primordial_pipe; base::Process child_process = LaunchChildTestClient( "SendMultipleIsolatedInvitationsClient", &primordial_pipe, 1, - TransportType::kChannel, MOJO_SEND_INVITATION_FLAG_ISOLATED, nullptr, 0, - &command_line, &options); + MOJO_SEND_INVITATION_FLAG_ISOLATED, nullptr, 0, &command_line, &options); secondary_transport.RemoteProcessLaunchAttempted(); WriteMessage(primordial_pipe, kTestMessage1); @@ -888,8 +802,8 @@ MojoHandle new_pipe; SendInvitationToClient( secondary_transport.TakeLocalEndpoint().TakePlatformHandle(), - child_process.Handle(), &new_pipe, 1, TransportType::kChannel, - MOJO_SEND_INVITATION_FLAG_ISOLATED, nullptr, 0, ""); + child_process.Handle(), &new_pipe, 1, MOJO_SEND_INVITATION_FLAG_ISOLATED, + nullptr, 0, ""); WaitForSignals(primordial_pipe, MOJO_HANDLE_SIGNAL_PEER_CLOSED); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(primordial_pipe)); @@ -952,17 +866,17 @@ PlatformChannel channel2; MojoHandle pipe0, pipe1; const char kConnectionName[] = "there can be only one!"; - SendInvitationToClient( - channel1.TakeLocalEndpoint().TakePlatformHandle(), - base::kNullProcessHandle, &pipe0, 1, TransportType::kChannel, - MOJO_SEND_INVITATION_FLAG_ISOLATED, nullptr, 0, kConnectionName); + SendInvitationToClient(channel1.TakeLocalEndpoint().TakePlatformHandle(), + base::kNullProcessHandle, &pipe0, 1, + MOJO_SEND_INVITATION_FLAG_ISOLATED, nullptr, 0, + kConnectionName); // Send another invitation with the same connection name. |pipe0| should be // disconnected as the first invitation's connection is torn down. - SendInvitationToClient( - channel2.TakeLocalEndpoint().TakePlatformHandle(), - base::kNullProcessHandle, &pipe1, 1, TransportType::kChannel, - MOJO_SEND_INVITATION_FLAG_ISOLATED, nullptr, 0, kConnectionName); + SendInvitationToClient(channel2.TakeLocalEndpoint().TakePlatformHandle(), + base::kNullProcessHandle, &pipe1, 1, + MOJO_SEND_INVITATION_FLAG_ISOLATED, nullptr, 0, + kConnectionName); WaitForSignals(pipe0, MOJO_HANDLE_SIGNAL_PEER_CLOSED); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(pipe0)); @@ -979,11 +893,9 @@ MojoHandle pipe0, pipe1; SendInvitationToClient(channel.TakeLocalEndpoint().TakePlatformHandle(), base::kNullProcessHandle, &pipe0, 1, - TransportType::kChannel, MOJO_SEND_INVITATION_FLAG_ISOLATED, nullptr, 0, ""); SendInvitationToClient(channel.TakeRemoteEndpoint().TakePlatformHandle(), base::kNullProcessHandle, &pipe1, 1, - TransportType::kChannel, MOJO_SEND_INVITATION_FLAG_ISOLATED, nullptr, 0, ""); WriteMessage(pipe0, kTestMessage1); @@ -994,9 +906,9 @@ TEST_F(InvitationTest, BrokenInvitationTransportBreaksAttachedPipe) { MojoHandle primordial_pipe; - base::Process child_process = LaunchChildTestClient( - "BrokenTransportClient", &primordial_pipe, 1, TransportType::kChannel, - MOJO_SEND_INVITATION_FLAG_NONE); + base::Process child_process = + LaunchChildTestClient("BrokenTransportClient", &primordial_pipe, 1, + MOJO_SEND_INVITATION_FLAG_NONE); EXPECT_EQ(MOJO_RESULT_OK, WaitForSignals(primordial_pipe, MOJO_HANDLE_SIGNAL_PEER_CLOSED)); @@ -1011,9 +923,9 @@ TEST_F(InvitationTest, BrokenIsolatedInvitationTransportBreaksAttachedPipe) { MojoHandle primordial_pipe; - base::Process child_process = LaunchChildTestClient( - "BrokenTransportClient", &primordial_pipe, 1, TransportType::kChannel, - MOJO_SEND_INVITATION_FLAG_ISOLATED); + base::Process child_process = + LaunchChildTestClient("BrokenTransportClient", &primordial_pipe, 1, + MOJO_SEND_INVITATION_FLAG_ISOLATED); EXPECT_EQ(MOJO_RESULT_OK, WaitForSignals(primordial_pipe, MOJO_HANDLE_SIGNAL_PEER_CLOSED));
diff --git a/mojo/core/ipcz_driver/invitation.cc b/mojo/core/ipcz_driver/invitation.cc index ef55e9a..80c4cf0 100644 --- a/mojo/core/ipcz_driver/invitation.cc +++ b/mojo/core/ipcz_driver/invitation.cc
@@ -89,15 +89,9 @@ return IPCZ_INVALID_DRIVER_HANDLE; } - Channel::Endpoint channel_endpoint; - if (endpoint.type == MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL_SERVER) { - channel_endpoint = PlatformChannelServerEndpoint(std::move(handle)); - } else { - channel_endpoint = PlatformChannelEndpoint(std::move(handle)); - } auto transport = base::MakeRefCounted<Transport>( - endpoint_types, std::move(channel_endpoint), std::move(remote_process), - is_remote_process_untrusted); + endpoint_types, PlatformChannelEndpoint(std::move(handle)), + std::move(remote_process), is_remote_process_untrusted); transport->SetErrorHandler(error_handler, error_handler_context); transport->set_leak_channel_on_shutdown(options.leak_channel_on_shutdown); transport->set_is_peer_trusted(options.is_peer_trusted);
diff --git a/mojo/core/ipcz_driver/transport.cc b/mojo/core/ipcz_driver/transport.cc index fa572f7..e797e92b 100644 --- a/mojo/core/ipcz_driver/transport.cc +++ b/mojo/core/ipcz_driver/transport.cc
@@ -25,7 +25,6 @@ #include "mojo/public/cpp/platform/platform_channel.h" #include "mojo/public/cpp/platform/platform_channel_endpoint.h" #include "mojo/public/cpp/platform/platform_handle.h" -#include "third_party/abseil-cpp/absl/types/variant.h" #include "third_party/ipcz/include/ipcz/ipcz.h" #if BUILDFLAG(IS_WIN) @@ -201,7 +200,7 @@ } // namespace Transport::Transport(EndpointTypes endpoint_types, - Channel::Endpoint endpoint, + PlatformChannelEndpoint endpoint, base::Process remote_process, bool is_remote_process_untrusted) : endpoint_types_(endpoint_types), @@ -214,7 +213,7 @@ // static scoped_refptr<Transport> Transport::Create(EndpointTypes endpoint_types, - Channel::Endpoint endpoint, + PlatformChannelEndpoint endpoint, base::Process remote_process, bool is_remote_process_untrusted) { return base::MakeRefCounted<Transport>(endpoint_types, std::move(endpoint), @@ -279,7 +278,7 @@ std::vector<PendingTransmission> pending_transmissions; { base::AutoLock lock(lock_); - if (channel_ || !IsEndpointValid()) { + if (channel_ || !inactive_endpoint_.is_valid()) { return false; } @@ -351,7 +350,7 @@ scoped_refptr<Channel> channel; { base::AutoLock lock(lock_); - if (IsEndpointValid()) { + if (inactive_endpoint_.is_valid()) { PendingTransmission transmission; transmission.bytes = std::vector<uint8_t>(data.begin(), data.end()); transmission.handles = std::move(platform_handles); @@ -589,10 +588,8 @@ DCHECK_EQ(handles.size(), 1u); #endif - DCHECK(IsEndpointValid()); - DCHECK(absl::holds_alternative<PlatformChannelEndpoint>(inactive_endpoint_)); - handles[0] = absl::get<PlatformChannelEndpoint>(inactive_endpoint_) - .TakePlatformHandle(); + CHECK(inactive_endpoint_.is_valid()); + handles[0] = inactive_endpoint_.TakePlatformHandle(); return true; } @@ -670,18 +667,6 @@ self = std::move(self_reference_for_channel_); } -bool Transport::IsEndpointValid() const { - return absl::visit(base::Overloaded{ - [](const PlatformChannelEndpoint& endpoint) { - return endpoint.is_valid(); - }, - [](const PlatformChannelServerEndpoint& endpoint) { - return endpoint.is_valid(); - }, - }, - inactive_endpoint_); -} - bool Transport::CanTransmitHandles() const { #if BUILDFLAG(IS_WIN) // On Windows, we can transmit handles only if at least one endpoint is a
diff --git a/mojo/core/ipcz_driver/transport.h b/mojo/core/ipcz_driver/transport.h index 1472037..9642910 100644 --- a/mojo/core/ipcz_driver/transport.h +++ b/mojo/core/ipcz_driver/transport.h
@@ -41,7 +41,7 @@ EndpointType destination; }; Transport(EndpointTypes endpoint_types, - Channel::Endpoint endpoint, + PlatformChannelEndpoint endpoint, base::Process remote_process, bool is_remote_process_untrusted = false); @@ -49,7 +49,7 @@ // than MakeRefCounted<T>. static scoped_refptr<Transport> Create( EndpointTypes endpoint_types, - Channel::Endpoint endpoint, + PlatformChannelEndpoint endpoint, base::Process remote_process = base::Process(), bool is_remote_process_untrusted = false); @@ -93,7 +93,7 @@ // invalidating the transport. May only be called on a Transport which has not // yet been activated, and only when the channel endpoint is not a server. PlatformChannelEndpoint TakeEndpoint() { - return std::move(absl::get<PlatformChannelEndpoint>(inactive_endpoint_)); + return std::move(inactive_endpoint_); } // Handles reports of bad activity from ipcz, resulting from parcel rejection @@ -168,7 +168,6 @@ ~Transport() override; - bool IsEndpointValid() const; bool CanTransmitHandles() const; // Indicates whether this transport should serialize its remote process handle @@ -209,7 +208,7 @@ // start its underlying Channel instance once activated. Not guarded by a lock // since it must not accessed beyond activation, where thread safety becomes a // factor. - Channel::Endpoint inactive_endpoint_; + PlatformChannelEndpoint inactive_endpoint_; base::Lock lock_; scoped_refptr<Channel> channel_ GUARDED_BY(lock_);
diff --git a/mojo/core/node_controller.cc b/mojo/core/node_controller.cc index b4998b0..5bb1939 100644 --- a/mojo/core/node_controller.cc +++ b/mojo/core/node_controller.cc
@@ -162,21 +162,6 @@ target_process.IsValid() ? target_process.Duplicate() : base::Process(), std::move(connection_params), process_error_callback); -#if BUILDFLAG(IS_WIN) - // On Windows, if target_process is invalid it means it's elevated or running - // in another session so a named pipe should be used instead. - if (!target_process.IsValid()) { - handle_policy = Channel::HandlePolicy::kRejectHandles; - NamedPlatformChannel::Options options; - NamedPlatformChannel named_channel(options); - node_connection_params = - ConnectionParams(named_channel.TakeServerEndpoint()); - node_connection_params.set_is_untrusted_process(is_untrusted_process); - broker_host->SendNamedChannel(named_channel.GetServerName()); - return node_connection_params; - } -#endif - // Sync connections usurp the passed endpoint and use it for the sync broker // channel. A new channel is created here for the NodeChannel and sent over // a sync broker message to the client. @@ -431,6 +416,48 @@ // |BIND_SYNC_BROKER| message from the invited client. node_connection_params = std::move(connection_params); } else { +#if defined(IS_WIN) + // On Windows, if `target_process` is invalid we can't duplicate a pipe + // handle to the remote client. In that case we instead open a new named + // pipe and send the client its name via the broker. Once connected, the new + // named pipe will be used for the client Channel. + if (!target_process.IsValid()) { + NamedPlatformChannel::Options options; + NamedPlatformChannel named_channel(options); + + const bool is_untrusted_process = + connection_params.is_untrusted_process(); + BrokerHost* broker_host = + new BrokerHost(base::Process(), std::move(connection_params), + process_error_callback); + broker_host->SendNamedChannel(named_channel.GetServerName()); + + PlatformChannelServer::WaitForConnection( + named_channel.TakeServerEndpoint(), + base::BindOnce( + [](base::Process target_process, + const ports::NodeName& temporary_node_name, + const ProcessErrorCallback& process_error_callback, + bool is_untrusted_process, + scoped_refptr<NodeController> node_controller, + PlatformChannelEndpoint endpoint) { + if (!endpoint.is_valid()) { + return; + } + + ConnectionParams params(std::move(endpoint)); + params.set_is_untrusted_process(is_untrusted_process); + node_controller->FinishSendBrokerClientInvitationOnIOThread( + std::move(target_process), std::move(params), + temporary_node_name, Channel::HandlePolicy::kRejectHandles, + process_error_callback); + }, + std::move(target_process), temporary_node_name, + process_error_callback, is_untrusted_process, this)); + return; + } +#endif + absl::optional<ConnectionParams> params = CreateSyncNodeConnectionParams( target_process, std::move(connection_params), process_error_callback, handle_policy); @@ -444,14 +471,26 @@ node_connection_params = std::move(*params); } - scoped_refptr<NodeChannel> channel = NodeChannel::Create( - this, std::move(node_connection_params), handle_policy, io_task_runner_, - process_error_callback); + FinishSendBrokerClientInvitationOnIOThread( + std::move(target_process), std::move(node_connection_params), + temporary_node_name, handle_policy, process_error_callback); #else // !BUILDFLAG(IS_APPLE) && !BUILDFLAG(IS_NACL) && !BUILDFLAG(IS_FUCHSIA) - scoped_refptr<NodeChannel> channel = NodeChannel::Create( - this, std::move(connection_params), Channel::HandlePolicy::kAcceptHandles, - io_task_runner_, process_error_callback); + FinishSendBrokerClientInvitationOnIOThread( + std::move(target_process), std::move(connection_params), + temporary_node_name, Channel::HandlePolicy::kAcceptHandles, + process_error_callback); #endif // !BUILDFLAG(IS_APPLE) && !BUILDFLAG(IS_NACL) && !BUILDFLAG(IS_FUCHSIA) +} + +void NodeController::FinishSendBrokerClientInvitationOnIOThread( + base::Process target_process, + ConnectionParams connection_params, + ports::NodeName temporary_node_name, + Channel::HandlePolicy handle_policy, + const ProcessErrorCallback& process_error_callback) { + scoped_refptr<NodeChannel> channel = + NodeChannel::Create(this, std::move(connection_params), handle_policy, + io_task_runner_, process_error_callback); // We set up the invitee channel with a temporary name so it can be identified // as a pending invitee if it writes any messages to the channel. We may start
diff --git a/mojo/core/node_controller.h b/mojo/core/node_controller.h index 80651533..70a914c 100644 --- a/mojo/core/node_controller.h +++ b/mojo/core/node_controller.h
@@ -188,8 +188,15 @@ void SendBrokerClientInvitationOnIOThread( base::Process target_process, ConnectionParams connection_params, - ports::NodeName token, + ports::NodeName temporary_node_name, const ProcessErrorCallback& process_error_callback); + void FinishSendBrokerClientInvitationOnIOThread( + base::Process target_process, + ConnectionParams connection_params, + ports::NodeName temporary_node_name, + Channel::HandlePolicy handle_policy, + const ProcessErrorCallback& process_error_callback); + void AcceptBrokerClientInvitationOnIOThread( ConnectionParams connection_params, absl::optional<PlatformHandle> broker_host_handle);
diff --git a/mojo/public/cpp/platform/BUILD.gn b/mojo/public/cpp/platform/BUILD.gn index 5c07c918..95326ae7 100644 --- a/mojo/public/cpp/platform/BUILD.gn +++ b/mojo/public/cpp/platform/BUILD.gn
@@ -28,9 +28,21 @@ sources += [ "socket_utils_posix.cc" ] } + if (is_win || is_mac || (is_posix && !is_nacl)) { + public += [ "platform_channel_server.h" ] + sources += [ "platform_channel_server.cc" ] + } + if (is_win) { public += [ "platform_handle_security_util_win.h" ] - sources += [ "platform_handle_security_util_win.cc" ] + sources += [ + "platform_channel_server_win.cc", + "platform_handle_security_util_win.cc", + ] + } else if (is_mac) { + sources += [ "platform_channel_server_mac.cc" ] + } else if (is_posix && !is_nacl) { + sources += [ "platform_channel_server_posix.cc" ] } public_deps = [
diff --git a/mojo/public/cpp/platform/named_platform_channel.h b/mojo/public/cpp/platform/named_platform_channel.h index c8e19615..455a9fa 100644 --- a/mojo/public/cpp/platform/named_platform_channel.h +++ b/mojo/public/cpp/platform/named_platform_channel.h
@@ -87,8 +87,8 @@ // named pipe server; on POSIX it's a bound, listening domain socket. In each // case it should accept a single new connection. // - // Use the handle to send or receive an invitation, with the endpoint type as - // |MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL_SERVER|. + // Use with PlatformChannelServer to wait for a new connection, yielding a + // PlatformChannelEndpoint that is usable with the Mojo invitations API. [[nodiscard]] PlatformChannelServerEndpoint TakeServerEndpoint() { return std::move(server_endpoint_); }
diff --git a/mojo/public/cpp/platform/platform_channel_server.cc b/mojo/public/cpp/platform/platform_channel_server.cc new file mode 100644 index 0000000..f9cb895 --- /dev/null +++ b/mojo/public/cpp/platform/platform_channel_server.cc
@@ -0,0 +1,52 @@ +// 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 "mojo/public/cpp/platform/platform_channel_server.h" + +#include <memory> +#include <utility> + +namespace mojo { + +PlatformChannelServer::PlatformChannelServer() = default; + +PlatformChannelServer::~PlatformChannelServer() = default; + +// static +void PlatformChannelServer::WaitForConnection( + PlatformChannelServerEndpoint server_endpoint, + ConnectionCallback callback) { + auto server = std::make_unique<PlatformChannelServer>(); + auto* server_ptr = server.get(); + auto wrapped_callback = base::BindOnce( + [](std::unique_ptr<PlatformChannelServer> server, + PlatformChannelServer::ConnectionCallback callback, + PlatformChannelEndpoint endpoint) { + std::move(callback).Run(std::move(endpoint)); + }, + std::move(server), std::move(callback)); + if (!server_ptr->TryListen(server_endpoint, wrapped_callback)) { + base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, + base::BindOnce(std::move(wrapped_callback), PlatformChannelEndpoint())); + } +} + +bool PlatformChannelServer::TryListen( + PlatformChannelServerEndpoint& server_endpoint, + ConnectionCallback& callback) { + auto listener = Listener::Create(); + if (!listener->Start(server_endpoint, callback)) { + return false; + } + + listener_ = std::move(listener); + return true; +} + +void PlatformChannelServer::Stop() { + listener_.reset(); +} + +} // namespace mojo
diff --git a/mojo/public/cpp/platform/platform_channel_server.h b/mojo/public/cpp/platform/platform_channel_server.h new file mode 100644 index 0000000..3d9db8a4 --- /dev/null +++ b/mojo/public/cpp/platform/platform_channel_server.h
@@ -0,0 +1,89 @@ +// 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 MOJO_PUBLIC_CPP_PLATFORM_PLATFORM_CHANNEL_SERVER_H_ +#define MOJO_PUBLIC_CPP_PLATFORM_PLATFORM_CHANNEL_SERVER_H_ + +#include <memory> + +#include "base/component_export.h" +#include "base/functional/callback.h" +#include "base/task/single_thread_task_runner.h" +#include "mojo/public/cpp/platform/platform_channel_endpoint.h" +#include "mojo/public/cpp/platform/platform_channel_server_endpoint.h" + +namespace mojo { + +// PlatformChannelServer takes ownership of a PlatformChannelServerEndpoint +// and listens for a single incoming client connection. +// +// This class is not thread-safe and must be used on a thread which runs an I/O +// MessagePump. +class COMPONENT_EXPORT(MOJO_CPP_PLATFORM) PlatformChannelServer { + public: + using ConnectionCallback = base::OnceCallback<void(PlatformChannelEndpoint)>; + + // Implemented for each supported platform. + class COMPONENT_EXPORT(MOJO_CPP_PLATFORM) Listener { + public: + virtual ~Listener() = default; + + // Implemented for each supported platform. + static std::unique_ptr<Listener> Create(); + + // Attempts to start listening on `server_endpoint`. Returns true on success + // or false on failure. Same semantics as Listen() below, which calls this. + virtual bool Start(PlatformChannelServerEndpoint& server_endpoint, + ConnectionCallback& callback) = 0; + }; + + PlatformChannelServer(); + + // Destruction implicitly stops the listener if started, ensuring the + // ConnectionCallback will not be called beyoned the lifetime of this object. + ~PlatformChannelServer(); + + // Spins up a PlatformChannelServer on the current (I/O) task runner to listen + // on `server_endpoint` for an incoming connection. `callback` is always + // called eventually, as long as the calling task runner is still running when + // either the server accepts a connection or is disconnected. If disconnected, + // `callback` receives an invalid endpoint. + static void WaitForConnection(PlatformChannelServerEndpoint server_endpoint, + ConnectionCallback callback); + + // Listens on `server_endpoint` for a single connection, invoking `callback` + // once it arrives. Must not be called on a server that is already listening. + // + // If the server endpoint is disconnected before a connection is received, + // the callback will be invoked with an invalid endpoint. + // + // If the server is stopped before a connection is received, `callback` will + // not be called. + // + // If the server could not listen on the given endpoint, this returns false + // and `callback` is never called. Otherwise it returns true. + // + // This takes ownership of (i.e. moves) `server_endpoint` and `callback` if + // and only if it returns true. + bool TryListen(PlatformChannelServerEndpoint& server_endpoint, + ConnectionCallback& callback); + + // Same as above, but takes a callback by value for convenience when the + // doesn't care about retaining the arguments in the failure case. + bool Listen(PlatformChannelServerEndpoint server_endpoint, + ConnectionCallback callback) { + return TryListen(server_endpoint, callback); + } + + // Stops listening for new connections immediately. The callback given to + // Listen() can no longer be invoked once this is called. + void Stop(); + + private: + std::unique_ptr<Listener> listener_; +}; + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_PLATFORM_PLATFORM_CHANNEL_SERVER_H_
diff --git a/mojo/public/cpp/platform/platform_channel_server_endpoint.h b/mojo/public/cpp/platform/platform_channel_server_endpoint.h index c741b87c..6229a076 100644 --- a/mojo/public/cpp/platform/platform_channel_server_endpoint.h +++ b/mojo/public/cpp/platform/platform_channel_server_endpoint.h
@@ -11,9 +11,10 @@ namespace mojo { // A PlatformHandle with a little extra type information to convey that it's -// a channel server endpoint, i.e. a handle that can be used to send invitations -// as |MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL_SERVER| to a remote -// PlatformChannelEndpoint. +// a channel server endpoint, i.e. a handle that should be used with +// PlatformChannelServer to wait for a new connection and ultimately provide +// a connected PlatformChannelEndpoint suitable for use with the Mojo +// invitations API. class COMPONENT_EXPORT(MOJO_CPP_PLATFORM) PlatformChannelServerEndpoint { public: PlatformChannelServerEndpoint();
diff --git a/mojo/public/cpp/platform/platform_channel_server_mac.cc b/mojo/public/cpp/platform/platform_channel_server_mac.cc new file mode 100644 index 0000000..3eb32fa --- /dev/null +++ b/mojo/public/cpp/platform/platform_channel_server_mac.cc
@@ -0,0 +1,66 @@ +// 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 "mojo/public/cpp/platform/platform_channel_server.h" + +#include <memory> +#include <utility> + +#include "base/check.h" +#include "base/memory/weak_ptr.h" +#include "base/task/sequenced_task_runner.h" +#include "mojo/public/cpp/platform/platform_channel_endpoint.h" +#include "mojo/public/cpp/platform/platform_handle.h" + +namespace mojo { + +namespace { + +// NOTE: On macOS, PlatformChannelServerEndpoint is not special, as they need to +// perform the same connection handshake as any other PlatformChannelEndpoint. +// PlatformChannelServer acts as a simple passthrough implementation for +// compatibility with application logic on other platforms. +class ListenerImpl : public PlatformChannelServer::Listener { + public: + ListenerImpl() = default; + ~ListenerImpl() override = default; + + // PlatformChannelServer::Listener: + bool Start(PlatformChannelServerEndpoint& server_endpoint, + PlatformChannelServer::ConnectionCallback& callback) override { + if (!server_endpoint.is_valid() || + !server_endpoint.platform_handle().is_mach_receive()) { + return false; + } + + // Invoke the callback asynchronously to guard against re-entrancy issues. + // This simply repackages the server endpoint as a PlatformChannelEndpoint, + // since they're functionally equivalent on macOS. Note that we post the + // task as a WeakPtr-bound method to ensure that it doesn't run if the + // Listener is destroyed first. + PlatformChannelEndpoint endpoint{server_endpoint.TakePlatformHandle()}; + base::SequencedTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, base::BindOnce(&ListenerImpl::RunCallback, + weak_ptr_factory_.GetWeakPtr(), + std::move(callback), std::move(endpoint))); + return true; + } + + private: + void RunCallback(PlatformChannelServer::ConnectionCallback callback, + PlatformChannelEndpoint endpoint) { + std::move(callback).Run(std::move(endpoint)); + } + + base::WeakPtrFactory<ListenerImpl> weak_ptr_factory_{this}; +}; + +} // namespace + +std::unique_ptr<PlatformChannelServer::Listener> +PlatformChannelServer::Listener::Create() { + return std::make_unique<ListenerImpl>(); +} + +} // namespace mojo
diff --git a/mojo/public/cpp/platform/platform_channel_server_posix.cc b/mojo/public/cpp/platform/platform_channel_server_posix.cc new file mode 100644 index 0000000..914c93e --- /dev/null +++ b/mojo/public/cpp/platform/platform_channel_server_posix.cc
@@ -0,0 +1,90 @@ +// 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 "mojo/public/cpp/platform/platform_channel_server.h" + +#include <memory> +#include <utility> + +#include "base/check.h" +#include "base/files/scoped_file.h" +#include "base/message_loop/message_pump_for_io.h" +#include "base/notreached.h" +#include "base/task/current_thread.h" +#include "mojo/public/cpp/platform/platform_channel_endpoint.h" +#include "mojo/public/cpp/platform/platform_handle.h" +#include "mojo/public/cpp/platform/socket_utils_posix.h" + +namespace mojo { + +namespace { + +class ListenerImpl : public PlatformChannelServer::Listener, + public base::MessagePumpForIO::FdWatcher { + public: + ListenerImpl() : watch_controller_(FROM_HERE) {} + ~ListenerImpl() override = default; + + // PlatformChannelServer::Listener: + bool Start(PlatformChannelServerEndpoint& server_endpoint, + PlatformChannelServer::ConnectionCallback& callback) override { + if (!server_endpoint.is_valid()) { + return false; + } + + base::ScopedFD server = server_endpoint.TakePlatformHandle().TakeFD(); + if (!base::CurrentIOThread::Get()->WatchFileDescriptor( + server.get(), /*persistent=*/true, + base::MessagePumpForIO::WATCH_READ, &watch_controller_, this)) { + return false; + } + + server_ = std::move(server); + callback_ = std::move(callback); + return true; + } + + // base::MessagePumpForIO::FdWatcher: + void OnFileCanReadWithoutBlocking(int fd) override { + base::ScopedFD socket; + CHECK_EQ(fd, server_.get()); + if (!AcceptSocketConnection(fd, &socket)) { + // Unrecoverable error, e.g. socket disconnection. Fail. + Stop(); + std::move(callback_).Run({}); + return; + } + + if (!socket.is_valid()) { + // Transient failure; a second connection attempt might succeed. + return; + } + + Stop(); + std::move(callback_).Run( + PlatformChannelEndpoint{PlatformHandle{std::move(socket)}}); + } + + void OnFileCanWriteWithoutBlocking(int fd) override { NOTREACHED(); } + + private: + void Stop() { + CHECK(server_.is_valid()); + watch_controller_.StopWatchingFileDescriptor(); + server_.reset(); + } + + base::ScopedFD server_; + PlatformChannelServer::ConnectionCallback callback_; + base::MessagePumpForIO::FdWatchController watch_controller_; +}; + +} // namespace + +std::unique_ptr<PlatformChannelServer::Listener> +PlatformChannelServer::Listener::Create() { + return std::make_unique<ListenerImpl>(); +} + +} // namespace mojo
diff --git a/mojo/public/cpp/platform/platform_channel_server_win.cc b/mojo/public/cpp/platform/platform_channel_server_win.cc new file mode 100644 index 0000000..08b31902f --- /dev/null +++ b/mojo/public/cpp/platform/platform_channel_server_win.cc
@@ -0,0 +1,134 @@ +// 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 "mojo/public/cpp/platform/platform_channel_server.h" + +#include <windows.h> + +#include <cstring> +#include <memory> +#include <utility> + +#include "base/check.h" +#include "base/memory/weak_ptr.h" +#include "base/task/current_thread.h" +#include "base/task/sequenced_task_runner.h" +#include "base/win/object_watcher.h" +#include "base/win/scoped_handle.h" +#include "mojo/public/cpp/platform/platform_channel_endpoint.h" +#include "mojo/public/cpp/platform/platform_handle.h" + +namespace mojo { + +namespace { + +class ListenerImpl : public PlatformChannelServer::Listener, + public base::win::ObjectWatcher::Delegate { + public: + ListenerImpl() { + memset(&connect_overlapped_, 0, sizeof(connect_overlapped_)); + } + + ~ListenerImpl() override { + if (server_.is_valid()) { + ::CancelIo(server_.get()); + } + } + + // PlatformChannelServer::Listener: + bool Start(PlatformChannelServerEndpoint& server_endpoint, + PlatformChannelServer::ConnectionCallback& callback) override { + connect_event_.Set(::CreateEvent(NULL, TRUE, FALSE, NULL)); + if (!connect_event_.is_valid()) { + return false; + } + + connect_overlapped_.hEvent = connect_event_.get(); + if (!event_watcher_.StartWatchingOnce(connect_event_.get(), this)) { + return false; + } + + base::win::ScopedHandle server = + server_endpoint.TakePlatformHandle().TakeHandle(); + BOOL ok = ::ConnectNamedPipe(server.get(), &connect_overlapped_); + if (ok) { + // This call should always fail with ERROR_IO_PENDING or + // ERROR_PIPE_CONNECTED. + return false; + } + + const DWORD error = ::GetLastError(); + switch (error) { + case ERROR_PIPE_CONNECTED: + // Already connected. Invoke the callback asynchronously to avoid any + // potential re-entrancy issues in the caller. The task is posted with + // a WeakPtr-bound method to ensure that it doesn't run if the server + // is stopped first. + event_watcher_.StopWatching(); + connect_event_.Close(); + base::SequencedTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, base::BindOnce(&ListenerImpl::RunCallback, + weak_ptr_factory_.GetWeakPtr(), + std::move(callback), std::move(server))); + break; + + case ERROR_IO_PENDING: + // Will continue in OnIOCompleted whenever the event is signaled. + break; + + default: + // Fail. + connect_event_.Close(); + return false; + } + + server_ = std::move(server); + callback_ = std::move(callback); + return true; + } + + // base::win::ObjectWatcher: + void OnObjectSignaled(HANDLE object) override { + // Event signaled. Check the status of the pipe. The only success case is + // when ConnectNamedPipe() returns ERROR_PIPE_CONNECTED here. + CHECK_EQ(object, connect_event_.get()); + BOOL ok = ::ConnectNamedPipe(server_.get(), &connect_overlapped_); + if (ok || ::GetLastError() != ERROR_PIPE_CONNECTED) { + std::move(callback_).Run({}); + return; + } + + // Success. Pass ownership of the connected pipe to the user-provided + // callback. + connect_event_.Close(); + std::move(callback_).Run( + PlatformChannelEndpoint{PlatformHandle{std::move(server_)}}); + } + + private: + void RunCallback(PlatformChannelServer::ConnectionCallback callback, + base::win::ScopedHandle server) { + std::move(callback).Run( + PlatformChannelEndpoint{PlatformHandle{std::move(server)}}); + } + + base::win::ScopedHandle server_; + + OVERLAPPED connect_overlapped_; + base::win::ScopedHandle connect_event_; + base::win::ObjectWatcher event_watcher_; + + PlatformChannelServer::ConnectionCallback callback_; + + base::WeakPtrFactory<ListenerImpl> weak_ptr_factory_{this}; +}; + +} // namespace + +std::unique_ptr<PlatformChannelServer::Listener> +PlatformChannelServer::Listener::Create() { + return std::make_unique<ListenerImpl>(); +} + +} // namespace mojo
diff --git a/mojo/public/cpp/platform/tests/BUILD.gn b/mojo/public/cpp/platform/tests/BUILD.gn index 8a7e7c9..66de64e 100644 --- a/mojo/public/cpp/platform/tests/BUILD.gn +++ b/mojo/public/cpp/platform/tests/BUILD.gn
@@ -14,4 +14,12 @@ "//mojo/public/cpp/system", "//testing/gtest", ] + + if (is_win || is_mac || (is_posix && !is_nacl)) { + sources += [ "platform_channel_server_unittest.cc" ] + deps += [ + "//base/test:test_support", + "//mojo/core/embedder", + ] + } }
diff --git a/mojo/public/cpp/platform/tests/DEPS b/mojo/public/cpp/platform/tests/DEPS index ef8ad28..d11274b53 100644 --- a/mojo/public/cpp/platform/tests/DEPS +++ b/mojo/public/cpp/platform/tests/DEPS
@@ -1,3 +1,4 @@ include_rules = [ + "+mojo/core", "+mojo/public", ]
diff --git a/mojo/public/cpp/platform/tests/platform_channel_server_unittest.cc b/mojo/public/cpp/platform/tests/platform_channel_server_unittest.cc new file mode 100644 index 0000000..48c1bc3 --- /dev/null +++ b/mojo/public/cpp/platform/tests/platform_channel_server_unittest.cc
@@ -0,0 +1,252 @@ +// 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 "mojo/public/cpp/platform/platform_channel_server.h" + +#include <tuple> +#include <utility> + +#include "base/containers/span.h" +#include "base/files/scoped_temp_dir.h" +#include "base/functional/callback.h" +#include "base/run_loop.h" +#include "base/task/single_thread_task_runner.h" +#include "base/test/bind.h" +#include "base/test/task_environment.h" +#include "build/build_config.h" +#include "mojo/core/channel.h" +#include "mojo/public/cpp/platform/named_platform_channel.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/abseil-cpp/absl/types/optional.h" + +namespace mojo { +namespace { + +class RunOnDestruction { + public: + template <typename Fn> + explicit RunOnDestruction(Fn fn) + : callback_(base::BindLambdaForTesting(fn)) {} + RunOnDestruction(RunOnDestruction&&) = default; + ~RunOnDestruction() { + if (callback_) { + std::move(callback_).Run(); + } + } + + private: + base::OnceClosure callback_; +}; + +class TestChannel : public core::Channel::Delegate { + public: + explicit TestChannel(PlatformChannelEndpoint endpoint) + : io_task_runner_(base::SingleThreadTaskRunner::GetCurrentDefault()), + channel_( + core::Channel::Create(this, + core::ConnectionParams{std::move(endpoint)}, + core::Channel::HandlePolicy::kRejectHandles, + io_task_runner_)) { + channel_->Start(); + } + + ~TestChannel() override { + // We pump the IO task queue after ShutDown() to ensure completion, as + // Channel implementions post a cleanup task there. + base::RunLoop shutdown_flush; + channel_->ShutDown(); + io_task_runner_->PostTask(FROM_HERE, shutdown_flush.QuitClosure()); + shutdown_flush.Run(); + } + + void SendMessage(const std::string& message) { + auto data = base::make_span( + reinterpret_cast<const uint8_t*>(message.data()), message.size()); + channel_->Write(core::Channel::Message::CreateIpczMessage(data, {})); + } + + std::string WaitForSingleMessage() { + wait_for_message_.Run(); + CHECK(received_message_); + return *received_message_; + } + + // core::Channel::Delegate: + bool IsIpczTransport() const override { + // We use Channel in ipcz mode because it's simpler. Doesn't matter if + // MojoIpcz is actually enabled. + return true; + } + + void OnChannelMessage(const void* payload, + size_t payload_size, + std::vector<PlatformHandle> handles) override { + received_message_ = + std::string(static_cast<const char*>(payload), payload_size); + std::move(quit_).Run(); + } + + void OnChannelError(core::Channel::Error error) override {} + + private: + const scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_; + const scoped_refptr<core::Channel> channel_; + base::RunLoop wait_for_message_; + base::OnceClosure quit_{wait_for_message_.QuitClosure()}; + absl::optional<std::string> received_message_; +}; + +class PlatformChannelServerTest : public testing::Test { + public: + PlatformChannelServerTest() { CHECK(temp_dir_.CreateUniqueTempDir()); } + + ~PlatformChannelServerTest() override = default; + + using NamedChannelDetails = std::tuple<PlatformChannelServerEndpoint, + NamedPlatformChannel::ServerName>; + NamedChannelDetails CreateRandomChannel() { + NamedPlatformChannel::Options options; +#if BUILDFLAG(IS_POSIX) + options.socket_dir = temp_dir_.GetPath(); +#endif + NamedPlatformChannel channel(options); + return {channel.TakeServerEndpoint(), channel.GetServerName()}; + } + + PlatformChannelServer& server() { return server_; } + + void VerifyEndToEndConnection(PlatformChannelEndpoint a, + PlatformChannelEndpoint b) { + TestChannel channel_a(std::move(a)); + TestChannel channel_b(std::move(b)); + + const std::string kMessage1 = "Hello, world?"; + const std::string kMessage2 = "Oh, hi world."; + channel_a.SendMessage(kMessage1); + channel_b.SendMessage(kMessage2); + EXPECT_EQ(kMessage2, channel_a.WaitForSingleMessage()); + EXPECT_EQ(kMessage1, channel_b.WaitForSingleMessage()); + } + + private: + base::ScopedTempDir temp_dir_; + base::test::TaskEnvironment task_environment_{ + base::test::TaskEnvironment::MainThreadType::IO}; + PlatformChannelServer server_; +}; + +TEST_F(PlatformChannelServerTest, ConnectAfterListen) { + // Basic test that a client can connect after Listen() and the server will + // invoke its user-provided callback with a new functioning endpoint. + auto [server_endpoint, name] = CreateRandomChannel(); + + base::RunLoop loop; + PlatformChannelEndpoint endpoint_a; + EXPECT_TRUE(server().Listen( + std::move(server_endpoint), + base::BindLambdaForTesting([&](PlatformChannelEndpoint endpoint) { + endpoint_a = std::move(endpoint); + loop.Quit(); + }))); + auto endpoint_b = NamedPlatformChannel::ConnectToServer(name); + EXPECT_TRUE(endpoint_b.is_valid()); + loop.Run(); + VerifyEndToEndConnection(std::move(endpoint_a), std::move(endpoint_b)); +} + +TEST_F(PlatformChannelServerTest, ConnectBeforeListen) { + // Basic test that a client can connect *before* Listen() and the server will + // still invoke its user-provided callback with a new functioning endpoint. + auto [server_endpoint, name] = CreateRandomChannel(); + + base::RunLoop loop; + auto endpoint_a = NamedPlatformChannel::ConnectToServer(name); + EXPECT_TRUE(endpoint_a.is_valid()); + PlatformChannelEndpoint endpoint_b; + EXPECT_TRUE(server().Listen( + std::move(server_endpoint), + base::BindLambdaForTesting([&](PlatformChannelEndpoint endpoint) { + endpoint_b = std::move(endpoint); + loop.Quit(); + }))); + loop.Run(); + VerifyEndToEndConnection(std::move(endpoint_a), std::move(endpoint_b)); +} + +TEST_F(PlatformChannelServerTest, WaitForConnection) { + // Tests the static WaitForConnection() helper. + auto [server_endpoint, name] = CreateRandomChannel(); + + base::RunLoop loop; + auto endpoint_a = NamedPlatformChannel::ConnectToServer(name); + PlatformChannelEndpoint endpoint_b; + PlatformChannelServer::WaitForConnection( + std::move(server_endpoint), + base::BindLambdaForTesting([&](PlatformChannelEndpoint endpoint) { + endpoint_b = std::move(endpoint); + loop.Quit(); + })); + loop.Run(); + VerifyEndToEndConnection(std::move(endpoint_a), std::move(endpoint_b)); +} + +TEST_F(PlatformChannelServerTest, NoCallbackAfterListenConnectStop) { + // Tests that the ConnectionCallback is never invoked after Stop(), even if + // we Listen() and the client connects immediately before the Stop() call. + auto [server_endpoint, name] = CreateRandomChannel(); + bool callback_invoked = false; + bool callback_destroyed = false; + base::RunLoop loop; + EXPECT_TRUE(server().Listen( + std::move(server_endpoint), + base::BindOnce( + // This callback should never run and should be destroyed when we + // Stop() below. + [](RunOnDestruction, bool* callback_invoked, + PlatformChannelEndpoint endpoint) { *callback_invoked = true; }, + // When the above callback is destroyed, this one will run. + RunOnDestruction([&] { + callback_destroyed = true; + loop.Quit(); + }), + &callback_invoked))); + auto endpoint = NamedPlatformChannel::ConnectToServer(name); + server().Stop(); + loop.Run(); + EXPECT_TRUE(callback_destroyed); + EXPECT_FALSE(callback_invoked); + EXPECT_TRUE(endpoint.is_valid()); +} + +TEST_F(PlatformChannelServerTest, NoCallbackAfterConnectListenStop) { + // Tests that the ConnectionCallback is never invoked after Stop(), even if + // the client connects before a Listen() which immediately precedes the Stop() + // call. + auto [server_endpoint, name] = CreateRandomChannel(); + bool callback_invoked = false; + bool callback_destroyed = false; + base::RunLoop loop; + auto endpoint = NamedPlatformChannel::ConnectToServer(name); + EXPECT_TRUE(endpoint.is_valid()); + EXPECT_TRUE(server().Listen( + std::move(server_endpoint), + base::BindOnce( + // This callback should never run and should be destroyed when we + // Stop() below. + [](RunOnDestruction, bool* callback_invoked, + PlatformChannelEndpoint endpoint) { *callback_invoked = true; }, + // When the above callback is destroyed, this one will run. + RunOnDestruction([&] { + callback_destroyed = true; + loop.Quit(); + }), + &callback_invoked))); + server().Stop(); + loop.Run(); + EXPECT_TRUE(callback_destroyed); + EXPECT_FALSE(callback_invoked); +} + +} // namespace +} // namespace mojo
diff --git a/mojo/public/cpp/system/invitation.cc b/mojo/public/cpp/system/invitation.cc index 7245722..42e0b46 100644 --- a/mojo/public/cpp/system/invitation.cc +++ b/mojo/public/cpp/system/invitation.cc
@@ -4,14 +4,22 @@ #include "mojo/public/cpp/system/invitation.h" +#include <memory> #include <tuple> +#include <utility> #include "base/numerics/safe_conversions.h" #include "build/build_config.h" +#include "mojo/core/embedder/embedder.h" #include "mojo/public/c/system/invitation.h" #include "mojo/public/c/system/platform_handle.h" +#include "mojo/public/cpp/platform/platform_channel_server.h" #include "mojo/public/cpp/system/platform_handle.h" +#if BUILDFLAG(IS_WIN) +#include <windows.h> +#endif + namespace mojo { namespace { @@ -102,6 +110,38 @@ std::ignore = invitation.release(); } +void WaitForServerConnection( + PlatformChannelServerEndpoint server_endpoint, + PlatformChannelServer::ConnectionCallback callback) { + core::GetIOTaskRunner()->PostTask( + FROM_HERE, + base::BindOnce(&PlatformChannelServer::WaitForConnection, + std::move(server_endpoint), std::move(callback))); +} + +base::Process CloneProcessFromHandle(base::ProcessHandle handle) { + if (handle == base::kNullProcessHandle) { + return base::Process{}; + } + +#if BUILDFLAG(IS_WIN) + // We can't use the hack below on Windows, because handle verification will + // explode when a new Process instance tries to own the already-owned + // `handle`. + HANDLE new_handle; + BOOL ok = + ::DuplicateHandle(::GetCurrentProcess(), handle, ::GetCurrentProcess(), + &new_handle, 0, FALSE, DUPLICATE_SAME_ACCESS); + CHECK(ok); + return base::Process(new_handle); +#else + base::Process temporary_owner(handle); + base::Process clone = temporary_owner.Duplicate(); + std::ignore = temporary_owner.Release(); + return clone; +#endif +} + } // namespace OutgoingInvitation::OutgoingInvitation() { @@ -169,10 +209,20 @@ base::ProcessHandle target_process, PlatformChannelServerEndpoint server_endpoint, const ProcessErrorCallback& error_callback) { - SendInvitation(std::move(invitation.handle_), target_process, - server_endpoint.TakePlatformHandle(), - MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL_SERVER, - invitation.extra_flags_, error_callback, ""); + WaitForServerConnection( + std::move(server_endpoint), + base::BindOnce( + [](OutgoingInvitation invitation, base::Process target_process, + const ProcessErrorCallback& error_callback, + PlatformChannelEndpoint endpoint) { + SendInvitation(std::move(invitation.handle_), + target_process.Handle(), + endpoint.TakePlatformHandle(), + MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL, + invitation.extra_flags_, error_callback, ""); + }, + std::move(invitation), CloneProcessFromHandle(target_process), + error_callback)); } // static @@ -191,7 +241,7 @@ PlatformChannelEndpoint channel_endpoint, base::StringPiece connection_name, base::ProcessHandle target_process) { - mojo::OutgoingInvitation invitation; + OutgoingInvitation invitation; ScopedMessagePipeHandle pipe = invitation.AttachMessagePipe(kIsolatedPipeName); SendInvitation(std::move(invitation.handle_), target_process, @@ -207,14 +257,24 @@ PlatformChannelServerEndpoint server_endpoint, base::StringPiece connection_name, base::ProcessHandle target_process) { - mojo::OutgoingInvitation invitation; + OutgoingInvitation invitation; ScopedMessagePipeHandle pipe = invitation.AttachMessagePipe(kIsolatedPipeName); - SendInvitation(std::move(invitation.handle_), target_process, - server_endpoint.TakePlatformHandle(), - MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL_SERVER, - MOJO_SEND_INVITATION_FLAG_ISOLATED | invitation.extra_flags_, - ProcessErrorCallback(), connection_name); + WaitForServerConnection( + std::move(server_endpoint), + base::BindOnce( + [](OutgoingInvitation invitation, base::Process target_process, + const std::string& connection_name, + PlatformChannelEndpoint endpoint) { + SendInvitation( + std::move(invitation.handle_), target_process.Handle(), + endpoint.TakePlatformHandle(), + MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL, + MOJO_SEND_INVITATION_FLAG_ISOLATED | invitation.extra_flags_, + ProcessErrorCallback(), connection_name); + }, + std::move(invitation), CloneProcessFromHandle(target_process), + std::string(connection_name))); return pipe; }
diff --git a/net/cert/cert_verifier.cc b/net/cert/cert_verifier.cc index 95450af..eb7873b 100644 --- a/net/cert/cert_verifier.cc +++ b/net/cert/cert_verifier.cc
@@ -21,6 +21,42 @@ namespace net { +namespace { + +class DefaultCertVerifyProcFactory : public net::CertVerifyProcFactory { + public: + scoped_refptr<net::CertVerifyProc> CreateCertVerifyProc( + scoped_refptr<net::CertNetFetcher> cert_net_fetcher, + const net::ChromeRootStoreData* root_store_data) override { + scoped_refptr<net::CertVerifyProc> verify_proc; +#if BUILDFLAG(CHROME_ROOT_STORE_OPTIONAL) + if (!verify_proc && + base::FeatureList::IsEnabled(features::kChromeRootStoreUsed)) { + verify_proc = CertVerifyProc::CreateBuiltinWithChromeRootStore( + std::move(cert_net_fetcher), root_store_data); + } +#endif + if (!verify_proc) { +#if BUILDFLAG(CHROME_ROOT_STORE_ONLY) + verify_proc = CertVerifyProc::CreateBuiltinWithChromeRootStore( + std::move(cert_net_fetcher), root_store_data); +#elif BUILDFLAG(IS_FUCHSIA) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) + verify_proc = + CertVerifyProc::CreateBuiltinVerifyProc(std::move(cert_net_fetcher)); +#else + verify_proc = + CertVerifyProc::CreateSystemVerifyProc(std::move(cert_net_fetcher)); +#endif + } + return verify_proc; + } + + private: + ~DefaultCertVerifyProcFactory() override = default; +}; + +} // namespace + CertVerifier::Config::Config() = default; CertVerifier::Config::Config(const Config&) = default; CertVerifier::Config::Config(Config&&) = default; @@ -79,28 +115,10 @@ // static std::unique_ptr<CertVerifier> CertVerifier::CreateDefaultWithoutCaching( scoped_refptr<CertNetFetcher> cert_net_fetcher) { - scoped_refptr<CertVerifyProc> verify_proc; -#if BUILDFLAG(CHROME_ROOT_STORE_OPTIONAL) - if (!verify_proc && - base::FeatureList::IsEnabled(features::kChromeRootStoreUsed)) { - verify_proc = CertVerifyProc::CreateBuiltinWithChromeRootStore( - std::move(cert_net_fetcher)); - } -#endif - if (!verify_proc) { -#if BUILDFLAG(CHROME_ROOT_STORE_ONLY) - verify_proc = CertVerifyProc::CreateBuiltinWithChromeRootStore( - std::move(cert_net_fetcher)); -#elif BUILDFLAG(IS_FUCHSIA) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) - verify_proc = - CertVerifyProc::CreateBuiltinVerifyProc(std::move(cert_net_fetcher)); -#else - verify_proc = - CertVerifyProc::CreateSystemVerifyProc(std::move(cert_net_fetcher)); -#endif - } - - return std::make_unique<MultiThreadedCertVerifier>(std::move(verify_proc)); + auto proc_factory = base::MakeRefCounted<DefaultCertVerifyProcFactory>(); + return std::make_unique<MultiThreadedCertVerifier>( + proc_factory->CreateCertVerifyProc(std::move(cert_net_fetcher), nullptr), + proc_factory); } // static
diff --git a/net/cert/cert_verify_proc.cc b/net/cert/cert_verify_proc.cc index 3854eb4..bcb5ad2 100644 --- a/net/cert/cert_verify_proc.cc +++ b/net/cert/cert_verify_proc.cc
@@ -441,11 +441,14 @@ #if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED) // static scoped_refptr<CertVerifyProc> CertVerifyProc::CreateBuiltinWithChromeRootStore( - scoped_refptr<CertNetFetcher> cert_net_fetcher) { + scoped_refptr<CertNetFetcher> cert_net_fetcher, + const ChromeRootStoreData* root_store_data) { + std::unique_ptr<TrustStoreChrome> chrome_root = + root_store_data ? std::make_unique<TrustStoreChrome>(*root_store_data) + : std::make_unique<TrustStoreChrome>(); return CreateCertVerifyProcBuiltin( std::move(cert_net_fetcher), - CreateSslSystemTrustStoreChromeRoot( - std::make_unique<net::TrustStoreChrome>())); + CreateSslSystemTrustStoreChromeRoot(std::move(chrome_root))); } #endif
diff --git a/net/cert/cert_verify_proc.h b/net/cert/cert_verify_proc.h index 68c0847..833b9e09 100644 --- a/net/cert/cert_verify_proc.h +++ b/net/cert/cert_verify_proc.h
@@ -84,9 +84,11 @@ #if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED) // Creates and returns a CertVerifyProcBuiltin using the Chrome Root Store - // SystemTrustStore. + // SystemTrustStore and the given |root_store_data|, which may be nullptr to + // use the default. static scoped_refptr<CertVerifyProc> CreateBuiltinWithChromeRootStore( - scoped_refptr<CertNetFetcher> cert_net_fetcher); + scoped_refptr<CertNetFetcher> cert_net_fetcher, + const ChromeRootStoreData* root_store_data); #endif CertVerifyProc(const CertVerifyProc&) = delete;
diff --git a/net/cert/multi_threaded_cert_verifier.cc b/net/cert/multi_threaded_cert_verifier.cc index 49d24604..ad375318 100644 --- a/net/cert/multi_threaded_cert_verifier.cc +++ b/net/cert/multi_threaded_cert_verifier.cc
@@ -194,14 +194,12 @@ } MultiThreadedCertVerifier::MultiThreadedCertVerifier( - scoped_refptr<CertVerifyProc> verify_proc) - : MultiThreadedCertVerifier(std::move(verify_proc), nullptr) {} - -MultiThreadedCertVerifier::MultiThreadedCertVerifier( scoped_refptr<CertVerifyProc> verify_proc, scoped_refptr<CertVerifyProcFactory> verify_proc_factory) : verify_proc_(std::move(verify_proc)), verify_proc_factory_(std::move(verify_proc_factory)) { + CHECK(verify_proc_); + CHECK(verify_proc_factory_); // Guarantee there is always a CRLSet (this can be overridden via SetConfig). config_.crl_set = CRLSet::BuiltinCRLSet(); } @@ -244,11 +242,8 @@ scoped_refptr<CertNetFetcher> cert_net_fetcher, const ChromeRootStoreData* root_store_data) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - // TODO(hchao): investigate to see if we can make this a DCHECK. - if (verify_proc_factory_) { - verify_proc_ = verify_proc_factory_->CreateCertVerifyProc( - std::move(cert_net_fetcher), root_store_data); - } + verify_proc_ = verify_proc_factory_->CreateCertVerifyProc( + std::move(cert_net_fetcher), root_store_data); } void MultiThreadedCertVerifier::SetConfig(const CertVerifier::Config& config) {
diff --git a/net/cert/multi_threaded_cert_verifier.h b/net/cert/multi_threaded_cert_verifier.h index 1300c3cd..642f4ad 100644 --- a/net/cert/multi_threaded_cert_verifier.h +++ b/net/cert/multi_threaded_cert_verifier.h
@@ -34,7 +34,6 @@ class NET_EXPORT_PRIVATE MultiThreadedCertVerifier : public CertVerifierWithUpdatableProc { public: - explicit MultiThreadedCertVerifier(scoped_refptr<CertVerifyProc> verify_proc); explicit MultiThreadedCertVerifier( scoped_refptr<CertVerifyProc> verify_proc, scoped_refptr<CertVerifyProcFactory> verify_proc_factory);
diff --git a/net/cert/test_root_certs_unittest.cc b/net/cert/test_root_certs_unittest.cc index abb998d..b66f1f506 100644 --- a/net/cert/test_root_certs_unittest.cc +++ b/net/cert/test_root_certs_unittest.cc
@@ -37,12 +37,12 @@ #if BUILDFLAG(CHROME_ROOT_STORE_OPTIONAL) if (base::FeatureList::IsEnabled(features::kChromeRootStoreUsed)) { return CertVerifyProc::CreateBuiltinWithChromeRootStore( - /*cert_net_fetcher=*/nullptr); + /*cert_net_fetcher=*/nullptr, /*root_store_data=*/nullptr); } #endif #if BUILDFLAG(CHROME_ROOT_STORE_ONLY) return CertVerifyProc::CreateBuiltinWithChromeRootStore( - /*cert_net_fetcher=*/nullptr); + /*cert_net_fetcher=*/nullptr, /*root_store_data=*/nullptr); #elif BUILDFLAG(IS_FUCHSIA) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) return CertVerifyProc::CreateBuiltinVerifyProc(/*cert_net_fetcher=*/nullptr); #else
diff --git a/net/cert/trial_comparison_cert_verifier_unittest.cc b/net/cert/trial_comparison_cert_verifier_unittest.cc index 06ee8ed..f20816d 100644 --- a/net/cert/trial_comparison_cert_verifier_unittest.cc +++ b/net/cert/trial_comparison_cert_verifier_unittest.cc
@@ -252,6 +252,11 @@ scoped_refptr<CertVerifyProc> verify_proc_; }; +scoped_refptr<CertVerifyProcFactory> SwapWithNotCalledProcFactory() { + return base::MakeRefCounted<SwapWithNewProcFactory>( + base::MakeRefCounted<NotCalledCertVerifyProc>()); +} + struct TrialReportInfo { TrialReportInfo(const std::string& hostname, const scoped_refptr<X509Certificate>& unverified_cert, @@ -347,8 +352,10 @@ auto verify_proc = base::MakeRefCounted<FakeCertVerifyProc>(OK, dummy_result); std::vector<TrialReportInfo> reports; TrialComparisonCertVerifier verifier( - verify_proc, nullptr, base::MakeRefCounted<NotCalledCertVerifyProc>(), - nullptr, base::BindRepeating(&RecordTrialReport, &reports)); + verify_proc, SwapWithNotCalledProcFactory(), + base::MakeRefCounted<NotCalledCertVerifyProc>(), + SwapWithNotCalledProcFactory(), + base::BindRepeating(&RecordTrialReport, &reports)); CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", /*flags=*/0, /*ocsp_response=*/std::string(), /*sct_list=*/std::string()); @@ -408,7 +415,8 @@ std::vector<TrialReportInfo> reports; TrialComparisonCertVerifier verifier( - verify_proc1, nullptr, verify_proc2, nullptr, + verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2, + SwapWithNotCalledProcFactory(), base::BindRepeating(&RecordTrialReport, &reports)); CertVerifier::RequestParams params(leaf, "t0.test", /*flags=*/0, @@ -491,7 +499,8 @@ std::vector<TrialReportInfo> reports; TrialComparisonCertVerifier verifier( - verify_proc1, nullptr, verify_proc2, nullptr, + verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2, + SwapWithNotCalledProcFactory(), base::BindRepeating(&RecordTrialReport, &reports)); verifier.set_trial_allowed(true); @@ -552,8 +561,10 @@ std::vector<TrialReportInfo> reports; TrialComparisonCertVerifier verifier( - verify_proc1, nullptr, base::MakeRefCounted<NotCalledCertVerifyProc>(), - nullptr, base::BindRepeating(&RecordTrialReport, &reports)); + verify_proc1, SwapWithNotCalledProcFactory(), + base::MakeRefCounted<NotCalledCertVerifyProc>(), + SwapWithNotCalledProcFactory(), + base::BindRepeating(&RecordTrialReport, &reports)); verifier.set_trial_allowed(true); CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", /*flags=*/0, @@ -588,6 +599,81 @@ EXPECT_TRUE(reports.empty()); } +TEST_F(TrialComparisonCertVerifierTest, ConfigChangedBeforeVerification) { + // Primary verifier returns an error status. + CertVerifyResult primary_result; + primary_result.verified_cert = cert_chain_1_; + primary_result.cert_status = CERT_STATUS_DATE_INVALID; + scoped_refptr<FakeCertVerifyProc> verify_proc1 = + base::MakeRefCounted<FakeCertVerifyProc>(ERR_CERT_DATE_INVALID, + primary_result); + + CertVerifyResult secondary_result; + secondary_result.verified_cert = cert_chain_1_; + scoped_refptr<FakeCertVerifyProc> verify_proc2 = + base::MakeRefCounted<FakeCertVerifyProc>(OK, secondary_result); + + std::vector<TrialReportInfo> reports; + // Both verifiers are initially NotCalledCertVerifyProc, but should swap to + // verify_proc1 and verify_proc2 when UpdateChromeRootStoreData is called. + TrialComparisonCertVerifier verifier( + base::MakeRefCounted<NotCalledCertVerifyProc>(), + base::MakeRefCounted<SwapWithNewProcFactory>(verify_proc1), + base::MakeRefCounted<NotCalledCertVerifyProc>(), + base::MakeRefCounted<SwapWithNewProcFactory>(verify_proc2), + base::BindRepeating(&RecordTrialReport, &reports)); + verifier.set_trial_allowed(true); + + // Change the verifier Chrome Root Store data before verification, so the + // Verify should call the verifiers that were swapped in by the factories + // instead of the initial ones. + verifier.UpdateChromeRootStoreData(nullptr, nullptr); + + CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", /*flags=*/0, + /*ocsp_response=*/std::string(), + /*sct_list=*/std::string()); + CertVerifyResult result; + TestCompletionCallback callback; + std::unique_ptr<CertVerifier::Request> request; + int error = verifier.Verify(params, &result, callback.callback(), &request, + NetLogWithSource()); + ASSERT_THAT(error, IsError(ERR_IO_PENDING)); + EXPECT_TRUE(request); + + error = callback.WaitForResult(); + EXPECT_THAT(error, IsError(ERR_CERT_DATE_INVALID)); + + verify_proc2->WaitForVerifyCall(); + RunUntilIdle(); + + // Expect a report. + ASSERT_EQ(1U, reports.size()); + const TrialReportInfo& report = reports[0]; + + EXPECT_EQ(CERT_STATUS_DATE_INVALID, report.primary_result.cert_status); + EXPECT_EQ(0U, report.trial_result.cert_status); + + EXPECT_TRUE(report.primary_result.verified_cert->EqualsIncludingChain( + cert_chain_1_.get())); + EXPECT_TRUE(report.trial_result.verified_cert->EqualsIncludingChain( + cert_chain_1_.get())); + EXPECT_TRUE(report.unverified_cert->EqualsIncludingChain(leaf_cert_1_.get())); + + EXPECT_FALSE(report.enable_rev_checking); + EXPECT_FALSE(report.require_rev_checking_local_anchors); + EXPECT_FALSE(report.enable_sha1_local_anchors); + EXPECT_FALSE(report.disable_symantec_enforcement); + + EXPECT_EQ(1, verify_proc1->num_verifications()); + EXPECT_EQ(1, verify_proc2->num_verifications()); + histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialPrimary", 1); + histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialSecondary", + 1); + histograms_.ExpectUniqueSample( + "Net.CertVerifier_TrialComparisonResult", + TrialComparisonResult::kPrimaryErrorSecondaryValid, 1); +} + TEST_F(TrialComparisonCertVerifierTest, CertVerifyProcChangedDuringPrimaryVerification) { CertVerifyResult primary_result; @@ -595,13 +681,12 @@ scoped_refptr<FakeCertVerifyProc> verify_proc1 = base::MakeRefCounted<FakeCertVerifyProc>(OK, primary_result); - scoped_refptr<SwapWithNewProcFactory> swap_proc = - base::MakeRefCounted<SwapWithNewProcFactory>( - base::MakeRefCounted<NotCalledCertVerifyProc>()); std::vector<TrialReportInfo> reports; TrialComparisonCertVerifier verifier( - verify_proc1, swap_proc, base::MakeRefCounted<NotCalledCertVerifyProc>(), - nullptr, base::BindRepeating(&RecordTrialReport, &reports)); + verify_proc1, SwapWithNotCalledProcFactory(), + base::MakeRefCounted<NotCalledCertVerifyProc>(), + SwapWithNotCalledProcFactory(), + base::BindRepeating(&RecordTrialReport, &reports)); verifier.set_trial_allowed(true); CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", /*flags=*/0, @@ -651,7 +736,8 @@ std::vector<TrialReportInfo> reports; TrialComparisonCertVerifier verifier( - verify_proc1, nullptr, verify_proc2, nullptr, + verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2, + SwapWithNotCalledProcFactory(), base::BindRepeating(&RecordTrialReport, &reports)); verifier.set_trial_allowed(true); @@ -710,12 +796,10 @@ base::MakeRefCounted<FakeCertVerifyProc>(ERR_CERT_DATE_INVALID, secondary_result); - scoped_refptr<SwapWithNewProcFactory> swap_proc = - base::MakeRefCounted<SwapWithNewProcFactory>( - base::MakeRefCounted<NotCalledCertVerifyProc>()); std::vector<TrialReportInfo> reports; TrialComparisonCertVerifier verifier( - verify_proc1, nullptr, verify_proc2, swap_proc, + verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2, + SwapWithNotCalledProcFactory(), base::BindRepeating(&RecordTrialReport, &reports)); verifier.set_trial_allowed(true); @@ -767,7 +851,8 @@ std::vector<TrialReportInfo> reports; TrialComparisonCertVerifier verifier( - verify_proc1, nullptr, verify_proc2, nullptr, + verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2, + SwapWithNotCalledProcFactory(), base::BindRepeating(&RecordTrialReport, &reports)); verifier.set_trial_allowed(true); @@ -816,7 +901,8 @@ std::vector<TrialReportInfo> reports; TrialComparisonCertVerifier verifier( - verify_proc1, nullptr, verify_proc2, nullptr, + verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2, + SwapWithNotCalledProcFactory(), base::BindRepeating(&RecordTrialReport, &reports)); verifier.set_trial_allowed(true); @@ -881,7 +967,8 @@ std::vector<TrialReportInfo> reports; TrialComparisonCertVerifier verifier( - verify_proc1, nullptr, verify_proc2, nullptr, + verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2, + SwapWithNotCalledProcFactory(), base::BindRepeating(&RecordTrialReport, &reports)); verifier.set_trial_allowed(true); @@ -945,7 +1032,8 @@ std::vector<TrialReportInfo> reports; TrialComparisonCertVerifier verifier( - verify_proc1, nullptr, verify_proc2, nullptr, + verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2, + SwapWithNotCalledProcFactory(), base::BindRepeating(&RecordTrialReport, &reports)); verifier.set_trial_allowed(true); @@ -1005,7 +1093,8 @@ std::vector<TrialReportInfo> reports; TrialComparisonCertVerifier verifier( - verify_proc1, nullptr, verify_proc2, nullptr, + verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2, + SwapWithNotCalledProcFactory(), base::BindRepeating(&RecordTrialReport, &reports)); verifier.set_trial_allowed(true); @@ -1068,7 +1157,8 @@ std::vector<TrialReportInfo> reports; TrialComparisonCertVerifier verifier( - verify_proc1, nullptr, verify_proc2, nullptr, + verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2, + SwapWithNotCalledProcFactory(), base::BindRepeating(&RecordTrialReport, &reports)); verifier.set_trial_allowed(true); @@ -1135,7 +1225,8 @@ std::vector<TrialReportInfo> reports; TrialComparisonCertVerifier verifier( - verify_proc1, nullptr, verify_proc2, nullptr, + verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2, + SwapWithNotCalledProcFactory(), base::BindRepeating(&RecordTrialReport, &reports)); verifier.set_trial_allowed(true); @@ -1231,7 +1322,8 @@ std::vector<TrialReportInfo> reports; TrialComparisonCertVerifier verifier( - verify_proc1, nullptr, verify_proc2, nullptr, + verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2, + SwapWithNotCalledProcFactory(), base::BindRepeating(&RecordTrialReport, &reports)); verifier.set_trial_allowed(true); @@ -1284,7 +1376,8 @@ std::vector<TrialReportInfo> reports; TrialComparisonCertVerifier verifier( - verify_proc1, nullptr, verify_proc2, nullptr, + verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2, + SwapWithNotCalledProcFactory(), base::BindRepeating(&RecordTrialReport, &reports)); verifier.set_trial_allowed(true); @@ -1356,7 +1449,8 @@ std::vector<TrialReportInfo> reports; TrialComparisonCertVerifier verifier( - verify_proc1, nullptr, verify_proc2, nullptr, + verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2, + SwapWithNotCalledProcFactory(), base::BindRepeating(&RecordTrialReport, &reports)); verifier.set_trial_allowed(true); @@ -1415,8 +1509,10 @@ std::vector<TrialReportInfo> reports; auto verifier = std::make_unique<TrialComparisonCertVerifier>( - verify_proc1, nullptr, base::MakeRefCounted<NotCalledCertVerifyProc>(), - nullptr, base::BindRepeating(&RecordTrialReport, &reports)); + verify_proc1, SwapWithNotCalledProcFactory(), + base::MakeRefCounted<NotCalledCertVerifyProc>(), + SwapWithNotCalledProcFactory(), + base::BindRepeating(&RecordTrialReport, &reports)); verifier->set_trial_allowed(true); CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", /*flags=*/0, @@ -1462,8 +1558,10 @@ std::vector<TrialReportInfo> reports; auto verifier = std::make_unique<TrialComparisonCertVerifier>( - verify_proc1, nullptr, base::MakeRefCounted<NotCalledCertVerifyProc>(), - nullptr, base::BindRepeating(&RecordTrialReport, &reports)); + verify_proc1, SwapWithNotCalledProcFactory(), + base::MakeRefCounted<NotCalledCertVerifyProc>(), + SwapWithNotCalledProcFactory(), + base::BindRepeating(&RecordTrialReport, &reports)); verifier->set_trial_allowed(true); CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", /*flags=*/0, @@ -1525,7 +1623,8 @@ bool was_report_callback_called = false; std::unique_ptr<TrialComparisonCertVerifier> verifier; verifier = std::make_unique<TrialComparisonCertVerifier>( - verify_proc1, nullptr, verify_proc2, nullptr, + verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2, + SwapWithNotCalledProcFactory(), base::BindLambdaForTesting( [&verifier, &was_report_callback_called]( const std::string& hostname, @@ -1590,7 +1689,8 @@ std::vector<TrialReportInfo> reports; auto verifier = std::make_unique<TrialComparisonCertVerifier>( - verify_proc1, nullptr, verify_proc2, nullptr, + verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2, + SwapWithNotCalledProcFactory(), base::BindRepeating(&RecordTrialReport, &reports)); verifier->set_trial_allowed(true); @@ -1665,7 +1765,8 @@ std::vector<TrialReportInfo> reports; TrialComparisonCertVerifier verifier( - verify_proc1, nullptr, verify_proc2, nullptr, + verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2, + SwapWithNotCalledProcFactory(), base::BindRepeating(&RecordTrialReport, &reports)); verifier.set_trial_allowed(true); @@ -1736,7 +1837,8 @@ std::vector<TrialReportInfo> reports; TrialComparisonCertVerifier verifier( - verify_proc1, nullptr, verify_proc2, nullptr, + verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2, + SwapWithNotCalledProcFactory(), base::BindRepeating(&RecordTrialReport, &reports)); verifier.set_trial_allowed(true); @@ -1808,7 +1910,8 @@ std::vector<TrialReportInfo> reports; TrialComparisonCertVerifier verifier( - verify_proc1, nullptr, verify_proc2, nullptr, + verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2, + SwapWithNotCalledProcFactory(), base::BindRepeating(&RecordTrialReport, &reports)); verifier.set_trial_allowed(true); @@ -1873,7 +1976,8 @@ std::vector<TrialReportInfo> reports; TrialComparisonCertVerifier verifier( - verify_proc1, nullptr, verify_proc2, nullptr, + verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2, + SwapWithNotCalledProcFactory(), base::BindRepeating(&RecordTrialReport, &reports)); verifier.set_trial_allowed(true); @@ -1942,7 +2046,8 @@ std::vector<TrialReportInfo> reports; TrialComparisonCertVerifier verifier( - verify_proc1, nullptr, verify_proc2, nullptr, + verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2, + SwapWithNotCalledProcFactory(), base::BindRepeating(&RecordTrialReport, &reports)); verifier.set_trial_allowed(true); @@ -1994,7 +2099,8 @@ std::vector<TrialReportInfo> reports; TrialComparisonCertVerifier verifier( - verify_proc1, nullptr, verify_proc2, nullptr, + verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2, + SwapWithNotCalledProcFactory(), base::BindRepeating(&RecordTrialReport, &reports)); verifier.set_trial_allowed(true); @@ -2047,7 +2153,8 @@ std::vector<TrialReportInfo> reports; TrialComparisonCertVerifier verifier( - verify_proc1, nullptr, verify_proc2, nullptr, + verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2, + SwapWithNotCalledProcFactory(), base::BindRepeating(&RecordTrialReport, &reports)); verifier.set_trial_allowed(true); @@ -2101,7 +2208,8 @@ std::vector<TrialReportInfo> reports; TrialComparisonCertVerifier verifier( - verify_proc1, nullptr, verify_proc2, nullptr, + verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2, + SwapWithNotCalledProcFactory(), base::BindRepeating(&RecordTrialReport, &reports)); verifier.set_trial_allowed(true); @@ -2156,7 +2264,8 @@ std::vector<TrialReportInfo> reports; TrialComparisonCertVerifier verifier( - verify_proc1, nullptr, verify_proc2, nullptr, + verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2, + SwapWithNotCalledProcFactory(), base::BindRepeating(&RecordTrialReport, &reports)); verifier.set_trial_allowed(true); @@ -2211,7 +2320,8 @@ std::vector<TrialReportInfo> reports; TrialComparisonCertVerifier verifier( - verify_proc1, nullptr, verify_proc2, nullptr, + verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2, + SwapWithNotCalledProcFactory(), base::BindRepeating(&RecordTrialReport, &reports)); verifier.set_trial_allowed(true); @@ -2269,7 +2379,8 @@ std::vector<TrialReportInfo> reports; TrialComparisonCertVerifier verifier( - verify_proc1, nullptr, verify_proc2, nullptr, + verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2, + SwapWithNotCalledProcFactory(), base::BindRepeating(&RecordTrialReport, &reports)); verifier.set_trial_allowed(true); @@ -2319,7 +2430,8 @@ std::vector<TrialReportInfo> reports; TrialComparisonCertVerifier verifier( - verify_proc1, nullptr, verify_proc2, nullptr, + verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2, + SwapWithNotCalledProcFactory(), base::BindRepeating(&RecordTrialReport, &reports)); verifier.set_trial_allowed(true); @@ -2371,7 +2483,8 @@ std::vector<TrialReportInfo> reports; TrialComparisonCertVerifier verifier( - verify_proc1, nullptr, verify_proc2, nullptr, + verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2, + SwapWithNotCalledProcFactory(), base::BindRepeating(&RecordTrialReport, &reports)); verifier.set_trial_allowed(true);
diff --git a/remoting/host/chromoting_host_services_client.cc b/remoting/host/chromoting_host_services_client.cc index 04337d5..9fcf5b53 100644 --- a/remoting/host/chromoting_host_services_client.cc +++ b/remoting/host/chromoting_host_services_client.cc
@@ -37,6 +37,19 @@ return {}; } #if BUILDFLAG(IS_WIN) + DWORD peer_session_id; + if (!GetNamedPipeServerSessionId(endpoint.platform_handle().GetHandle().get(), + &peer_session_id)) { + PLOG(ERROR) << "GetNamedPipeServerSessionId failed"; + return {}; + } + // '0' (default) corresponds to the session the network process runs in. + if (peer_session_id != 0) { + LOG(ERROR) + << "Cannot establish connection with IPC server running in session: " + << peer_session_id; + return {}; + } // TODO(crbug.com/1378803): Make Windows hosts work with non-isolated // connections. auto message_pipe = connection.Connect(std::move(endpoint));
diff --git a/services/cert_verifier/trial_comparison_cert_verifier_mojo_unittest.cc b/services/cert_verifier/trial_comparison_cert_verifier_mojo_unittest.cc index 849f3210..bf944f0 100644 --- a/services/cert_verifier/trial_comparison_cert_verifier_mojo_unittest.cc +++ b/services/cert_verifier/trial_comparison_cert_verifier_mojo_unittest.cc
@@ -5,6 +5,7 @@ #include "services/cert_verifier/trial_comparison_cert_verifier_mojo.h" #include "base/containers/span.h" +#include "base/memory/scoped_refptr.h" #include "base/test/task_environment.h" #include "build/build_config.h" #include "net/cert/cert_verify_proc.h" @@ -28,6 +29,8 @@ #include "net/cert/internal/trust_store_nss.h" #endif +namespace { + struct ReceivedReport { std::string hostname; scoped_refptr<net::X509Certificate> unverified_cert; @@ -94,6 +97,43 @@ base::RunLoop run_loop_; }; +class NotCalledCertVerifyProc : public net::CertVerifyProc { + public: + bool SupportsAdditionalTrustAnchors() const override { return false; } + + protected: + ~NotCalledCertVerifyProc() override = default; + + private: + int VerifyInternal(net::X509Certificate* cert, + const std::string& hostname, + const std::string& ocsp_response, + const std::string& sct_list, + int flags, + net::CRLSet* crl_set, + const net::CertificateList& additional_trust_anchors, + net::CertVerifyResult* verify_result, + const net::NetLogWithSource& net_log) override { + ADD_FAILURE() << "NotCalledCertVerifyProc was called!"; + return net::ERR_UNEXPECTED; + } +}; + +class NotCalledProcFactory : public net::CertVerifyProcFactory { + public: + scoped_refptr<net::CertVerifyProc> CreateCertVerifyProc( + scoped_refptr<net::CertNetFetcher> cert_net_fetcher, + const net::ChromeRootStoreData* root_store_data) override { + ADD_FAILURE() << "NotCalledProcFactory was called!"; + return nullptr; + } + + protected: + ~NotCalledProcFactory() override = default; +}; + +} // namespace + TEST(TrialComparisonCertVerifierMojoTest, SendReportDebugInfo) { base::test::TaskEnvironment scoped_task_environment; @@ -158,8 +198,11 @@ FakeReportClient report_client( report_client_remote.InitWithNewPipeAndPassReceiver()); cert_verifier::TrialComparisonCertVerifierMojo tccvm( - true, {}, std::move(report_client_remote), nullptr, nullptr, nullptr, - nullptr); + true, {}, std::move(report_client_remote), + base::MakeRefCounted<NotCalledCertVerifyProc>(), + base::MakeRefCounted<NotCalledProcFactory>(), + base::MakeRefCounted<NotCalledCertVerifyProc>(), + base::MakeRefCounted<NotCalledProcFactory>()); tccvm.OnSendTrialReport("example.com", unverified_cert, false, false, false, false, "stapled ocsp", "sct list", primary_result,
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json index 3ddb4da..22b3cc3 100644 --- a/testing/buildbot/chromium.chromiumos.json +++ b/testing/buildbot/chromium.chromiumos.json
@@ -5886,9 +5886,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5677.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5679.0/test_ash_chrome" ], - "description": "Run with ash-chrome version 114.0.5677.0", + "description": "Run with ash-chrome version 114.0.5679.0", "isolate_profile_data": true, "merge": { "args": [], @@ -5900,8 +5900,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v114.0.5677.0", - "revision": "version:114.0.5677.0" + "location": "lacros_version_skew_tests_v114.0.5679.0", + "revision": "version:114.0.5679.0" } ], "dimension_sets": [ @@ -6057,9 +6057,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5677.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5679.0/test_ash_chrome" ], - "description": "Run with ash-chrome version 114.0.5677.0", + "description": "Run with ash-chrome version 114.0.5679.0", "isolate_profile_data": true, "merge": { "args": [], @@ -6071,8 +6071,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v114.0.5677.0", - "revision": "version:114.0.5677.0" + "location": "lacros_version_skew_tests_v114.0.5679.0", + "revision": "version:114.0.5679.0" } ], "dimension_sets": [ @@ -6209,9 +6209,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5677.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5679.0/test_ash_chrome" ], - "description": "Run with ash-chrome version 114.0.5677.0", + "description": "Run with ash-chrome version 114.0.5679.0", "isolate_profile_data": true, "merge": { "args": [], @@ -6223,8 +6223,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v114.0.5677.0", - "revision": "version:114.0.5677.0" + "location": "lacros_version_skew_tests_v114.0.5679.0", + "revision": "version:114.0.5679.0" } ], "dimension_sets": [
diff --git a/testing/buildbot/chromium.coverage.json b/testing/buildbot/chromium.coverage.json index 80d20332..8a5749f 100644 --- a/testing/buildbot/chromium.coverage.json +++ b/testing/buildbot/chromium.coverage.json
@@ -25738,9 +25738,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5677.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5679.0/test_ash_chrome" ], - "description": "Run with ash-chrome version 114.0.5677.0", + "description": "Run with ash-chrome version 114.0.5679.0", "isolate_profile_data": true, "merge": { "args": [], @@ -25752,8 +25752,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v114.0.5677.0", - "revision": "version:114.0.5677.0" + "location": "lacros_version_skew_tests_v114.0.5679.0", + "revision": "version:114.0.5679.0" } ], "dimension_sets": [ @@ -25909,9 +25909,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5677.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5679.0/test_ash_chrome" ], - "description": "Run with ash-chrome version 114.0.5677.0", + "description": "Run with ash-chrome version 114.0.5679.0", "isolate_profile_data": true, "merge": { "args": [], @@ -25923,8 +25923,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v114.0.5677.0", - "revision": "version:114.0.5677.0" + "location": "lacros_version_skew_tests_v114.0.5679.0", + "revision": "version:114.0.5679.0" } ], "dimension_sets": [ @@ -26061,9 +26061,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5677.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5679.0/test_ash_chrome" ], - "description": "Run with ash-chrome version 114.0.5677.0", + "description": "Run with ash-chrome version 114.0.5679.0", "isolate_profile_data": true, "merge": { "args": [], @@ -26075,8 +26075,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v114.0.5677.0", - "revision": "version:114.0.5677.0" + "location": "lacros_version_skew_tests_v114.0.5679.0", + "revision": "version:114.0.5679.0" } ], "dimension_sets": [
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json index 946050d..87df94e9 100644 --- a/testing/buildbot/chromium.fyi.json +++ b/testing/buildbot/chromium.fyi.json
@@ -46385,9 +46385,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5677.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5679.0/test_ash_chrome" ], - "description": "Run with ash-chrome version 114.0.5677.0", + "description": "Run with ash-chrome version 114.0.5679.0", "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" @@ -46398,8 +46398,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v114.0.5677.0", - "revision": "version:114.0.5677.0" + "location": "lacros_version_skew_tests_v114.0.5679.0", + "revision": "version:114.0.5679.0" } ], "dimension_sets": [ @@ -46556,9 +46556,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5677.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5679.0/test_ash_chrome" ], - "description": "Run with ash-chrome version 114.0.5677.0", + "description": "Run with ash-chrome version 114.0.5679.0", "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" @@ -46569,8 +46569,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v114.0.5677.0", - "revision": "version:114.0.5677.0" + "location": "lacros_version_skew_tests_v114.0.5679.0", + "revision": "version:114.0.5679.0" } ], "dimension_sets": [ @@ -46708,9 +46708,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5677.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5679.0/test_ash_chrome" ], - "description": "Run with ash-chrome version 114.0.5677.0", + "description": "Run with ash-chrome version 114.0.5679.0", "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" @@ -46721,8 +46721,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v114.0.5677.0", - "revision": "version:114.0.5677.0" + "location": "lacros_version_skew_tests_v114.0.5679.0", + "revision": "version:114.0.5679.0" } ], "dimension_sets": [ @@ -48228,9 +48228,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5677.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5679.0/test_ash_chrome" ], - "description": "Run with ash-chrome version 114.0.5677.0", + "description": "Run with ash-chrome version 114.0.5679.0", "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" @@ -48241,8 +48241,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v114.0.5677.0", - "revision": "version:114.0.5677.0" + "location": "lacros_version_skew_tests_v114.0.5679.0", + "revision": "version:114.0.5679.0" } ], "dimension_sets": [ @@ -48399,9 +48399,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5677.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5679.0/test_ash_chrome" ], - "description": "Run with ash-chrome version 114.0.5677.0", + "description": "Run with ash-chrome version 114.0.5679.0", "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" @@ -48412,8 +48412,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v114.0.5677.0", - "revision": "version:114.0.5677.0" + "location": "lacros_version_skew_tests_v114.0.5679.0", + "revision": "version:114.0.5679.0" } ], "dimension_sets": [ @@ -48551,9 +48551,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5677.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5679.0/test_ash_chrome" ], - "description": "Run with ash-chrome version 114.0.5677.0", + "description": "Run with ash-chrome version 114.0.5679.0", "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" @@ -48564,8 +48564,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v114.0.5677.0", - "revision": "version:114.0.5677.0" + "location": "lacros_version_skew_tests_v114.0.5679.0", + "revision": "version:114.0.5679.0" } ], "dimension_sets": [ @@ -49319,9 +49319,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5677.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5679.0/test_ash_chrome" ], - "description": "Run with ash-chrome version 114.0.5677.0", + "description": "Run with ash-chrome version 114.0.5679.0", "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" @@ -49332,8 +49332,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v114.0.5677.0", - "revision": "version:114.0.5677.0" + "location": "lacros_version_skew_tests_v114.0.5679.0", + "revision": "version:114.0.5679.0" } ], "dimension_sets": [
diff --git a/testing/buildbot/chromium.memory.json b/testing/buildbot/chromium.memory.json index 7463e1d..5264360 100644 --- a/testing/buildbot/chromium.memory.json +++ b/testing/buildbot/chromium.memory.json
@@ -18414,12 +18414,12 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5677.0/test_ash_chrome", + "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5679.0/test_ash_chrome", "--test-launcher-print-test-stdio=always", "--combine-ash-logs-on-bots", "--asan-symbolize-output" ], - "description": "Run with ash-chrome version 114.0.5677.0", + "description": "Run with ash-chrome version 114.0.5679.0", "isolate_profile_data": true, "merge": { "args": [], @@ -18431,8 +18431,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v114.0.5677.0", - "revision": "version:114.0.5677.0" + "location": "lacros_version_skew_tests_v114.0.5679.0", + "revision": "version:114.0.5679.0" } ], "dimension_sets": [ @@ -18605,12 +18605,12 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5677.0/test_ash_chrome", + "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5679.0/test_ash_chrome", "--test-launcher-print-test-stdio=always", "--combine-ash-logs-on-bots", "--asan-symbolize-output" ], - "description": "Run with ash-chrome version 114.0.5677.0", + "description": "Run with ash-chrome version 114.0.5679.0", "isolate_profile_data": true, "merge": { "args": [], @@ -18622,8 +18622,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v114.0.5677.0", - "revision": "version:114.0.5677.0" + "location": "lacros_version_skew_tests_v114.0.5679.0", + "revision": "version:114.0.5679.0" } ], "dimension_sets": [ @@ -18772,12 +18772,12 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5677.0/test_ash_chrome", + "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5679.0/test_ash_chrome", "--test-launcher-print-test-stdio=always", "--combine-ash-logs-on-bots", "--asan-symbolize-output" ], - "description": "Run with ash-chrome version 114.0.5677.0", + "description": "Run with ash-chrome version 114.0.5679.0", "isolate_profile_data": true, "merge": { "args": [], @@ -18789,8 +18789,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v114.0.5677.0", - "revision": "version:114.0.5677.0" + "location": "lacros_version_skew_tests_v114.0.5679.0", + "revision": "version:114.0.5679.0" } ], "dimension_sets": [
diff --git a/testing/buildbot/variants.pyl b/testing/buildbot/variants.pyl index 5bc11ac..b638ad3 100644 --- a/testing/buildbot/variants.pyl +++ b/testing/buildbot/variants.pyl
@@ -22,16 +22,16 @@ }, 'LACROS_VERSION_SKEW_CANARY': { 'args': [ - '--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5677.0/test_ash_chrome', + '--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5679.0/test_ash_chrome', ], - 'description': 'Run with ash-chrome version 114.0.5677.0', + 'description': 'Run with ash-chrome version 114.0.5679.0', 'identifier': 'Lacros version skew testing ash canary', 'swarming': { 'cipd_packages': [ { 'cipd_package': 'chromium/testing/linux-ash-chromium/x86_64/ash.zip', - 'location': 'lacros_version_skew_tests_v114.0.5677.0', - 'revision': 'version:114.0.5677.0', + 'location': 'lacros_version_skew_tests_v114.0.5679.0', + 'revision': 'version:114.0.5679.0', }, ], },
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json index 7ad0648..0d4e7420 100644 --- a/testing/variations/fieldtrial_testing_config.json +++ b/testing/variations/fieldtrial_testing_config.json
@@ -6081,6 +6081,26 @@ ] } ], + "HttpsUpgrades": [ + { + "platforms": [ + "android", + "windows", + "chromeos", + "chromeos_lacros", + "mac", + "linux" + ], + "experiments": [ + { + "name": "Enabled", + "enable_features": [ + "HttpsUpgrades" + ] + } + ] + } + ], "IOSAddToHomeScreen": [ { "platforms": [
diff --git a/third_party/blink/renderer/core/css/css_math_expression_node.cc b/third_party/blink/renderer/core/css/css_math_expression_node.cc index 24afe1d..cce7d83 100644 --- a/third_party/blink/renderer/core/css/css_math_expression_node.cc +++ b/third_party/blink/renderer/core/css/css_math_expression_node.cc
@@ -38,6 +38,7 @@ #include "third_party/blink/renderer/core/css/css_primitive_value.h" #include "third_party/blink/renderer/core/css/css_primitive_value_mappings.h" #include "third_party/blink/renderer/core/css/css_value_clamping_utils.h" +#include "third_party/blink/renderer/core/css/parser/css_parser_context.h" #include "third_party/blink/renderer/core/css/properties/css_parsing_utils.h" #include "third_party/blink/renderer/core/css/resolver/style_resolver.h" #include "third_party/blink/renderer/core/css_value_keywords.h"
diff --git a/third_party/blink/renderer/core/css/css_math_expression_node.h b/third_party/blink/renderer/core/css/css_math_expression_node.h index 5359ff2..606f7ae 100644 --- a/third_party/blink/renderer/core/css/css_math_expression_node.h +++ b/third_party/blink/renderer/core/css/css_math_expression_node.h
@@ -39,7 +39,6 @@ #include "third_party/blink/renderer/core/css/css_math_operator.h" #include "third_party/blink/renderer/core/css/css_primitive_value.h" #include "third_party/blink/renderer/core/css/css_value.h" -#include "third_party/blink/renderer/core/css/parser/css_parser_context.h" #include "third_party/blink/renderer/core/css/parser/css_parser_token_range.h" #include "third_party/blink/renderer/platform/geometry/calculation_value.h" #include "third_party/blink/renderer/platform/wtf/forward.h" @@ -50,6 +49,7 @@ class CalculationExpressionNode; class CSSNumericLiteralValue; +class CSSParserContext; // The order of this enum should not change since its elements are used as // indices in the addSubtractResult matrix.
diff --git a/third_party/blink/renderer/core/css/css_properties.json5 b/third_party/blink/renderer/core/css/css_properties.json5 index bd9ff4b2..33f1c93 100644 --- a/third_party/blink/renderer/core/css/css_properties.json5 +++ b/third_party/blink/renderer/core/css/css_properties.json5
@@ -3608,7 +3608,6 @@ field_template: "primitive", default_value: "0", type_name: "int", - computed_style_custom_functions: ["setter"], typedom_types: ["Number"], }, {
diff --git a/third_party/blink/renderer/core/css/cssom/css_numeric_value.cc b/third_party/blink/renderer/core/css/cssom/css_numeric_value.cc index 5797db6..999c366c 100644 --- a/third_party/blink/renderer/core/css/cssom/css_numeric_value.cc +++ b/third_party/blink/renderer/core/css/cssom/css_numeric_value.cc
@@ -20,6 +20,7 @@ #include "third_party/blink/renderer/core/css/cssom/css_math_product.h" #include "third_party/blink/renderer/core/css/cssom/css_math_sum.h" #include "third_party/blink/renderer/core/css/cssom/css_unit_value.h" +#include "third_party/blink/renderer/core/css/parser/css_parser_context.h" #include "third_party/blink/renderer/core/css/parser/css_parser_token_stream.h" #include "third_party/blink/renderer/core/css/parser/css_tokenizer.h" #include "third_party/blink/renderer/core/css_value_keywords.h"
diff --git a/third_party/blink/renderer/core/css/style_engine.cc b/third_party/blink/renderer/core/css/style_engine.cc index 8b07b57..92f07f5 100644 --- a/third_party/blink/renderer/core/css/style_engine.cc +++ b/third_party/blink/renderer/core/css/style_engine.cc
@@ -34,6 +34,7 @@ #include "base/hash/hash.h" #include "base/ranges/algorithm.h" #include "third_party/blink/public/mojom/frame/color_scheme.mojom-blink.h" +#include "third_party/blink/public/mojom/timing/resource_timing.mojom-blink.h" #include "third_party/blink/public/platform/web_theme_engine.h" #include "third_party/blink/renderer/core/css/cascade_layer_map.h" #include "third_party/blink/renderer/core/css/check_pseudo_has_cache_scope.h"
diff --git a/third_party/blink/renderer/core/html/html_link_element.h b/third_party/blink/renderer/core/html/html_link_element.h index e81a7b6..041552c 100644 --- a/third_party/blink/renderer/core/html/html_link_element.h +++ b/third_party/blink/renderer/core/html/html_link_element.h
@@ -27,6 +27,7 @@ #include <memory> #include "base/task/single_thread_task_runner.h" +#include "services/network/public/mojom/referrer_policy.mojom-shared.h" #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/css/css_style_sheet.h" #include "third_party/blink/renderer/core/dom/create_element_flags.h"
diff --git a/third_party/blink/renderer/core/html/parser/preload_request.h b/third_party/blink/renderer/core/html/parser/preload_request.h index 4486012..dad33a8 100644 --- a/third_party/blink/renderer/core/html/parser/preload_request.h +++ b/third_party/blink/renderer/core/html/parser/preload_request.h
@@ -8,7 +8,7 @@ #include <memory> #include "base/memory/ptr_util.h" -#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink-forward.h" +#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h" #include "third_party/blink/public/mojom/script/script_type.mojom-blink-forward.h" #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/script/script.h"
diff --git a/third_party/blink/renderer/core/layout/build.gni b/third_party/blink/renderer/core/layout/build.gni index 127763c..a480c21 100644 --- a/third_party/blink/renderer/core/layout/build.gni +++ b/third_party/blink/renderer/core/layout/build.gni
@@ -650,8 +650,6 @@ "ng/table/ng_table_row_layout_algorithm.h", "ng/table/ng_table_section_layout_algorithm.cc", "ng/table/ng_table_section_layout_algorithm.h", - "order_iterator.cc", - "order_iterator.h", "overflow_model.h", "pointer_events_hit_rules.cc", "pointer_events_hit_rules.h",
diff --git a/third_party/blink/renderer/core/layout/flexible_box_algorithm.h b/third_party/blink/renderer/core/layout/flexible_box_algorithm.h index 50434562..5612453b 100644 --- a/third_party/blink/renderer/core/layout/flexible_box_algorithm.h +++ b/third_party/blink/renderer/core/layout/flexible_box_algorithm.h
@@ -37,7 +37,6 @@ #include "third_party/blink/renderer/core/layout/ng/ng_baseline_utils.h" #include "third_party/blink/renderer/core/layout/ng/ng_block_node.h" #include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h" -#include "third_party/blink/renderer/core/layout/order_iterator.h" #include "third_party/blink/renderer/core/style/computed_style.h" #include "third_party/blink/renderer/platform/geometry/layout_point.h" #include "third_party/blink/renderer/platform/geometry/layout_unit.h"
diff --git a/third_party/blink/renderer/core/layout/order_iterator.cc b/third_party/blink/renderer/core/layout/order_iterator.cc deleted file mode 100644 index 550126c..0000000 --- a/third_party/blink/renderer/core/layout/order_iterator.cc +++ /dev/null
@@ -1,89 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "third_party/blink/renderer/core/layout/order_iterator.h" - -#include "third_party/blink/renderer/core/layout/layout_box.h" - -namespace blink { - -OrderIterator::OrderIterator(const LayoutBox* container_box) - : container_box_(container_box) {} - -LayoutBox* OrderIterator::First() { - Reset(); - return Next(); -} - -LayoutBox* OrderIterator::Next() { - do { - if (!current_child_) { - if (order_values_iterator_ == order_values_.end()) - return nullptr; - - if (!is_reset_) { - ++order_values_iterator_; - if (order_values_iterator_ == order_values_.end()) - return nullptr; - } else { - is_reset_ = false; - } - - current_child_ = container_box_->FirstChildBox(); - } else { - current_child_ = current_child_->NextSiblingBox(); - } - } while (!current_child_ || - ResolvedOrder(*current_child_) != *order_values_iterator_); - - return current_child_; -} - -void OrderIterator::Reset() { - current_child_.Clear(); - order_values_iterator_ = order_values_.begin(); - is_reset_ = true; -} - -int OrderIterator::ResolvedOrder(const LayoutBox& child) const { - if (container_box_->StyleRef().IsDeprecatedWebkitBox()) - return child.StyleRef().BoxOrdinalGroup(); - return child.StyleRef().Order(); -} - -OrderIteratorPopulator::~OrderIteratorPopulator() { - iterator_.Reset(); -} - -void OrderIteratorPopulator::CollectChild(const LayoutBox* child) { - iterator_.order_values_.insert(iterator_.ResolvedOrder(*child)); -} - -} // namespace blink
diff --git a/third_party/blink/renderer/core/layout/order_iterator.h b/third_party/blink/renderer/core/layout/order_iterator.h deleted file mode 100644 index b06142c..0000000 --- a/third_party/blink/renderer/core/layout/order_iterator.h +++ /dev/null
@@ -1,108 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_ORDER_ITERATOR_H_ -#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_ORDER_ITERATOR_H_ - -#include "third_party/blink/renderer/platform/allow_discouraged_type.h" -#include "third_party/blink/renderer/platform/heap/member.h" -#include "third_party/blink/renderer/platform/heap/visitor.h" -#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" - -#include <set> - -namespace blink { - -class LayoutBox; - -class OrderIterator { - DISALLOW_NEW(); - - public: - friend class OrderIteratorPopulator; - - explicit OrderIterator(const LayoutBox*); - OrderIterator(const OrderIterator&) = delete; - OrderIterator& operator=(const OrderIterator&) = delete; - - LayoutBox* CurrentChild() { return current_child_; } - const LayoutBox* CurrentChild() const { return current_child_; } - LayoutBox* First(); - const LayoutBox* First() const { - return const_cast<OrderIterator*>(this)->First(); - } - LayoutBox* Next(); - const LayoutBox* Next() const { - return const_cast<OrderIterator*>(this)->Next(); - } - - void Trace(Visitor* visitor) const { - visitor->Trace(container_box_); - visitor->Trace(current_child_); - } - - private: - void Reset(); - - // Returns the order to use for |child|. - int ResolvedOrder(const LayoutBox& child) const; - - Member<const LayoutBox> container_box_; - - Member<LayoutBox> current_child_; - - using OrderValues ALLOW_DISCOURAGED_TYPE("TODO(crbug.com/1404327") = - std::set<int>; - OrderValues order_values_; - OrderValues::const_iterator order_values_iterator_; - // Set by |Reset()|, triggers iteration to start from the beginning. - bool is_reset_ = false; -}; - -class OrderIteratorPopulator { - STACK_ALLOCATED(); - - public: - explicit OrderIteratorPopulator(OrderIterator& iterator) - : iterator_(iterator) { - iterator_.order_values_.clear(); - } - - ~OrderIteratorPopulator(); - - void CollectChild(const LayoutBox*); - - private: - OrderIterator& iterator_; -}; - -} // namespace blink - -#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_ORDER_ITERATOR_H_
diff --git a/third_party/blink/renderer/core/loader/link_loader.cc b/third_party/blink/renderer/core/loader/link_loader.cc index 075cd10..fdf4137 100644 --- a/third_party/blink/renderer/core/loader/link_loader.cc +++ b/third_party/blink/renderer/core/loader/link_loader.cc
@@ -31,6 +31,7 @@ #include "third_party/blink/renderer/core/loader/link_loader.h" +#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-shared.h" #include "third_party/blink/renderer/core/dom/document.h" #include "third_party/blink/renderer/core/frame/local_dom_window.h" #include "third_party/blink/renderer/core/frame/local_frame.h"
diff --git a/third_party/blink/renderer/core/loader/resource/resource_loader_code_cache_test.cc b/third_party/blink/renderer/core/loader/resource/resource_loader_code_cache_test.cc index 4e8752e..d741ea6 100644 --- a/third_party/blink/renderer/core/loader/resource/resource_loader_code_cache_test.cc +++ b/third_party/blink/renderer/core/loader/resource/resource_loader_code_cache_test.cc
@@ -4,6 +4,7 @@ #include "base/task/single_thread_task_runner.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h" #include "third_party/blink/renderer/core/loader/resource/script_resource.h" #include "third_party/blink/renderer/platform/exported/wrapped_resource_response.h" #include "third_party/blink/renderer/platform/loader/fetch/cached_metadata.h"
diff --git a/third_party/blink/renderer/core/loader/threadable_loader.cc b/third_party/blink/renderer/core/loader/threadable_loader.cc index f19bece0..4f6eea1 100644 --- a/third_party/blink/renderer/core/loader/threadable_loader.cc +++ b/third_party/blink/renderer/core/loader/threadable_loader.cc
@@ -38,6 +38,7 @@ #include "services/network/public/cpp/cors/cors_error_status.h" #include "services/network/public/mojom/cors.mojom-blink.h" #include "services/network/public/mojom/fetch_api.mojom-blink.h" +#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h" #include "third_party/blink/public/platform/task_type.h" #include "third_party/blink/renderer/core/frame/frame_console.h" #include "third_party/blink/renderer/core/frame/local_dom_window.h"
diff --git a/third_party/blink/renderer/core/paint/block_painter.cc b/third_party/blink/renderer/core/paint/block_painter.cc index 7663e73..1eab9805 100644 --- a/third_party/blink/renderer/core/paint/block_painter.cc +++ b/third_party/blink/renderer/core/paint/block_painter.cc
@@ -166,16 +166,6 @@ ObjectPainter(child).PaintAllPhasesAtomically(float_paint_info); } -void BlockPainter::PaintChildrenAtomically(const OrderIterator& order_iterator, - const PaintInfo& paint_info) { - if (paint_info.DescendantPaintingBlocked()) - return; - for (const LayoutBox* child = order_iterator.First(); child; - child = order_iterator.Next()) { - PaintAllChildPhasesAtomically(*child, paint_info); - } -} - void BlockPainter::PaintAllChildPhasesAtomically(const LayoutBox& child, const PaintInfo& paint_info) { if (paint_info.DescendantPaintingBlocked())
diff --git a/third_party/blink/renderer/core/paint/block_painter.h b/third_party/blink/renderer/core/paint/block_painter.h index 42acbfd..a524e50 100644 --- a/third_party/blink/renderer/core/paint/block_painter.h +++ b/third_party/blink/renderer/core/paint/block_painter.h
@@ -7,7 +7,6 @@ #include "base/gtest_prod_util.h" #include "third_party/blink/renderer/core/core_export.h" -#include "third_party/blink/renderer/core/layout/order_iterator.h" #include "third_party/blink/renderer/platform/geometry/layout_rect.h" #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" @@ -35,7 +34,6 @@ // See ObjectPainter::PaintAllPhasesAtomically(). void PaintAllChildPhasesAtomically(const LayoutBox&, const PaintInfo&); - void PaintChildrenAtomically(const OrderIterator&, const PaintInfo&); static void PaintInlineBox(const InlineBox&, const PaintInfo&); private:
diff --git a/third_party/blink/renderer/core/speculation_rules/speculation_rules_header.cc b/third_party/blink/renderer/core/speculation_rules/speculation_rules_header.cc index 041f3a2..e48cad4 100644 --- a/third_party/blink/renderer/core/speculation_rules/speculation_rules_header.cc +++ b/third_party/blink/renderer/core/speculation_rules/speculation_rules_header.cc
@@ -11,6 +11,7 @@ #include "third_party/blink/public/common/origin_trials/trial_token.h" #include "third_party/blink/public/common/origin_trials/trial_token_result.h" #include "third_party/blink/public/mojom/devtools/console_message.mojom-shared.h" +#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-shared.h" #include "third_party/blink/renderer/core/execution_context/execution_context.h" #include "third_party/blink/renderer/core/execution_context/security_context.h" #include "third_party/blink/renderer/core/frame/local_dom_window.h"
diff --git a/third_party/blink/renderer/core/style/computed_style.h b/third_party/blink/renderer/core/style/computed_style.h index 31ee4653..a476a12 100644 --- a/third_party/blink/renderer/core/style/computed_style.h +++ b/third_party/blink/renderer/core/style/computed_style.h
@@ -3179,14 +3179,6 @@ SetOpacityInternal(v); } - // order - void SetOrder(int o) { - // TODO(ikilpatrick): Remove this setter once OrderIterator is removed. - // We restrict the smallest value to int min + 2 because we use int min and - // int min + 1 as special values in a hash set. - SetOrderInternal(max(std::numeric_limits<int>::min() + 2, o)); - } - // orphans void SetOrphans(int16_t o) { SetOrphansInternal(ClampTo<int16_t>(o, 1)); }
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_command_encoder.idl b/third_party/blink/renderer/modules/webgpu/gpu_command_encoder.idl index c17f860..314dcd1 100644 --- a/third_party/blink/renderer/modules/webgpu/gpu_command_encoder.idl +++ b/third_party/blink/renderer/modules/webgpu/gpu_command_encoder.idl
@@ -44,7 +44,7 @@ GPUBuffer destination, GPUSize64 destinationOffset); - [RaisesException] void writeTimestamp(GPUQuerySet querySet, GPUSize32 queryIndex); + [NoAllocDirectCall, RaisesException] void writeTimestamp(GPUQuerySet querySet, GPUSize32 queryIndex); [NoAllocDirectCall] void clearBuffer( GPUBuffer buffer,
diff --git a/third_party/blink/renderer/platform/graphics/paint/paint_controller.cc b/third_party/blink/renderer/platform/graphics/paint/paint_controller.cc index 5c30a6a..1b2c9e4 100644 --- a/third_party/blink/renderer/platform/graphics/paint/paint_controller.cc +++ b/third_party/blink/renderer/platform/graphics/paint/paint_controller.cc
@@ -742,6 +742,17 @@ num_indexed_items_ = 0; num_sequential_matches_ = 0; num_out_of_order_matches_ = 0; + + if (VLOG_IS_ON(1)) { + VLOG(1) << "PaintController::CommitNewDisplayItems() completed"; + if (VLOG_IS_ON(3)) { + ShowDebugDataWithPaintRecords(); + } else if (VLOG_IS_ON(2)) { + ShowDebugData(); + } else if (VLOG_IS_ON(1)) { + ShowCompactDebugData(); + } + } #endif } @@ -781,18 +792,6 @@ for (auto& chunk : current_paint_artifact_->PaintChunks()) chunk.client_is_just_created = false; - -#if DCHECK_IS_ON() - if (VLOG_IS_ON(1)) { - VLOG(1) << "PaintController::FinishCycle() completed"; - if (VLOG_IS_ON(3)) - ShowDebugDataWithPaintRecords(); - else if (VLOG_IS_ON(2)) - ShowDebugData(); - else if (VLOG_IS_ON(1)) - ShowCompactDebugData(); - } -#endif } size_t PaintController::ApproximateUnsharedMemoryUsage() const {
diff --git a/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.cc b/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.cc index 989a6277..5d7ae49 100644 --- a/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.cc +++ b/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.cc
@@ -297,7 +297,7 @@ } cc::YUVSubsampling AVIFImageDecoder::GetYUVSubsampling() const { - switch (decoder_->image->yuvFormat) { + switch (avif_yuv_format_) { case AVIF_PIXEL_FORMAT_YUV420: return cc::YUVSubsampling::k420; case AVIF_PIXEL_FORMAT_YUV422: @@ -307,10 +307,16 @@ case AVIF_PIXEL_FORMAT_YUV400: return cc::YUVSubsampling::kUnknown; case AVIF_PIXEL_FORMAT_NONE: - case AVIF_PIXEL_FORMAT_COUNT: - NOTREACHED(); + // avif_yuv_format_ is initialized to AVIF_PIXEL_FORMAT_NONE in the + // constructor. If we have called SetSize() successfully at the end + // of UpdateDemuxer(), avif_yuv_format_ cannot possibly be + // AVIF_PIXEL_FORMAT_NONE. + CHECK(!IsDecodedSizeAvailable()); return cc::YUVSubsampling::kUnknown; + case AVIF_PIXEL_FORMAT_COUNT: + break; } + NOTREACHED_NORETURN() << "Invalid YUV format: " << avif_yuv_format_; } gfx::Size AVIFImageDecoder::DecodedYUVSize(cc::YUVIndex index) const { @@ -849,6 +855,15 @@ ImageIsHighBitDepth() && high_bit_depth_decoding_option_ == kHighBitDepthToHalfFloat; + // Verify that AVIF_PIXEL_FORMAT_{YUV444,YUV422,YUV420,YUV400} are + // consecutive. + static_assert(AVIF_PIXEL_FORMAT_YUV422 == AVIF_PIXEL_FORMAT_YUV444 + 1); + static_assert(AVIF_PIXEL_FORMAT_YUV420 == AVIF_PIXEL_FORMAT_YUV422 + 1); + static_assert(AVIF_PIXEL_FORMAT_YUV400 == AVIF_PIXEL_FORMAT_YUV420 + 1); + // Assert that container->yuvFormat is one of the four YUV formats in AV1. + CHECK(container->yuvFormat >= AVIF_PIXEL_FORMAT_YUV444 && + container->yuvFormat <= AVIF_PIXEL_FORMAT_YUV400) + << "Invalid YUV format: " << container->yuvFormat; avif_yuv_format_ = container->yuvFormat; avifPixelFormatInfo format_info; avifGetPixelFormatInfo(container->yuvFormat, &format_info);
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource.cc b/third_party/blink/renderer/platform/loader/fetch/resource.cc index 8207141..973a077 100644 --- a/third_party/blink/renderer/platform/loader/fetch/resource.cc +++ b/third_party/blink/renderer/platform/loader/fetch/resource.cc
@@ -34,6 +34,7 @@ #include "base/time/default_clock.h" #include "build/build_config.h" #include "services/network/public/mojom/fetch_api.mojom-blink.h" +#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-shared.h" #include "third_party/blink/public/platform/platform.h" #include "third_party/blink/public/platform/web_security_origin.h" #include "third_party/blink/renderer/platform/heap/collection_support/heap_vector.h"
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h index a20e403..ca741d72 100644 --- a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h +++ b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h
@@ -40,7 +40,7 @@ #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink-forward.h" #include "third_party/blink/public/mojom/loader/resource_cache.mojom-blink.h" #include "third_party/blink/public/mojom/service_worker/controller_service_worker_mode.mojom-blink-forward.h" -#include "third_party/blink/public/mojom/timing/resource_timing.mojom-blink-forward.h" +#include "third_party/blink/public/mojom/timing/resource_timing.mojom-blink.h" #include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_map.h" #include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_set.h" #include "third_party/blink/renderer/platform/heap/collection_support/heap_vector.h"
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc b/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc index fcfcc1d3..fc7c1e0 100644 --- a/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc +++ b/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc
@@ -53,6 +53,7 @@ #include "third_party/blink/public/common/thread_safe_browser_interface_broker_proxy.h" #include "third_party/blink/public/mojom/blob/blob_registry.mojom-blink.h" #include "third_party/blink/public/mojom/devtools/console_message.mojom-blink.h" +#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h" #include "third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom-shared.h" #include "third_party/blink/public/platform/platform.h" #include "third_party/blink/public/platform/web_code_cache_loader.h"
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_response.cc b/third_party/blink/renderer/platform/loader/fetch/resource_response.cc index c14e253b..79aaa06 100644 --- a/third_party/blink/renderer/platform/loader/fetch/resource_response.cc +++ b/third_party/blink/renderer/platform/loader/fetch/resource_response.cc
@@ -35,6 +35,7 @@ #include "net/ssl/ssl_info.h" #include "services/network/public/cpp/cors/cors.h" #include "services/network/public/mojom/fetch_api.mojom-blink.h" +#include "third_party/blink/public/mojom/timing/resource_timing.mojom-blink.h" #include "third_party/blink/public/platform/web_url_response.h" #include "third_party/blink/renderer/platform/loader/fetch/resource_load_timing.h" #include "third_party/blink/renderer/platform/network/http_names.h"
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_response.h b/third_party/blink/renderer/platform/loader/fetch/resource_response.h index 9c50fb8..7ee6413b 100644 --- a/third_party/blink/renderer/platform/loader/fetch/resource_response.h +++ b/third_party/blink/renderer/platform/loader/fetch/resource_response.h
@@ -35,12 +35,11 @@ #include "net/base/ip_endpoint.h" #include "net/ssl/ssl_info.h" #include "services/network/public/cpp/trigger_attestation.h" -#include "services/network/public/mojom/alternate_protocol_usage.mojom-shared.h" -#include "services/network/public/mojom/cross_origin_embedder_policy.mojom-shared.h" +#include "services/network/public/mojom/cross_origin_embedder_policy.mojom-forward.h" #include "services/network/public/mojom/fetch_api.mojom-shared.h" #include "services/network/public/mojom/ip_address_space.mojom-shared.h" #include "third_party/abseil-cpp/absl/types/optional.h" -#include "third_party/blink/public/mojom/timing/resource_timing.mojom-blink.h" +#include "third_party/blink/public/mojom/timing/resource_timing.mojom-blink-forward.h" #include "third_party/blink/public/platform/web_url_response.h" #include "third_party/blink/renderer/platform/network/http_header_map.h" #include "third_party/blink/renderer/platform/network/http_parsers.h"
diff --git a/third_party/blink/renderer/platform/loader/fetch/url_loader/cached_metadata_handler.cc b/third_party/blink/renderer/platform/loader/fetch/url_loader/cached_metadata_handler.cc index 2ab1349a..1ce7df0 100644 --- a/third_party/blink/renderer/platform/loader/fetch/url_loader/cached_metadata_handler.cc +++ b/third_party/blink/renderer/platform/loader/fetch/url_loader/cached_metadata_handler.cc
@@ -5,6 +5,7 @@ #include "third_party/blink/renderer/platform/loader/fetch/url_loader/cached_metadata_handler.h" #include "base/time/time.h" +#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h" #include "third_party/blink/public/mojom/loader/code_cache.mojom-blink.h" #include "third_party/blink/public/platform/platform.h" #include "third_party/blink/public/platform/web_security_origin.h"
diff --git a/third_party/blink/renderer/platform/loader/fetch/url_loader/worker_main_script_loader.cc b/third_party/blink/renderer/platform/loader/fetch/url_loader/worker_main_script_loader.cc index fdb8149..e39be3dd 100644 --- a/third_party/blink/renderer/platform/loader/fetch/url_loader/worker_main_script_loader.cc +++ b/third_party/blink/renderer/platform/loader/fetch/url_loader/worker_main_script_loader.cc
@@ -10,6 +10,7 @@ #include "services/network/public/mojom/url_response_head.mojom.h" #include "third_party/blink/public/common/loader/referrer_utils.h" #include "third_party/blink/public/mojom/loader/code_cache.mojom-blink.h" +#include "third_party/blink/public/mojom/timing/resource_timing.mojom-blink.h" #include "third_party/blink/public/platform/resource_load_info_notifier_wrapper.h" #include "third_party/blink/public/platform/url_conversion.h" #include "third_party/blink/public/platform/web_url.h"
diff --git a/third_party/blink/renderer/platform/loader/testing/mock_fetch_context.h b/third_party/blink/renderer/platform/loader/testing/mock_fetch_context.h index cc39a2d..11aec65 100644 --- a/third_party/blink/renderer/platform/loader/testing/mock_fetch_context.h +++ b/third_party/blink/renderer/platform/loader/testing/mock_fetch_context.h
@@ -8,7 +8,7 @@ #include "base/task/single_thread_task_runner.h" #include "third_party/abseil-cpp/absl/types/optional.h" #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink-forward.h" -#include "third_party/blink/public/mojom/timing/resource_timing.mojom-blink-forward.h" +#include "third_party/blink/public/mojom/timing/resource_timing.mojom-blink.h" #include "third_party/blink/public/platform/platform.h" #include "third_party/blink/public/platform/resource_load_info_notifier_wrapper.h" #include "third_party/blink/public/platform/weak_wrapper_resource_load_info_notifier.h"
diff --git a/third_party/blink/tools/blinkpy/common/net/results_fetcher_test.py b/third_party/blink/tools/blinkpy/common/net/results_fetcher_test.py index adb0290..f07824b 100644 --- a/third_party/blink/tools/blinkpy/common/net/results_fetcher_test.py +++ b/third_party/blink/tools/blinkpy/common/net/results_fetcher_test.py
@@ -176,6 +176,70 @@ 'foo(bar)'), 'https://storage.googleapis.com/foo_bar_') + def test_gather_results(self): + self.fetcher.web.append_prpc_response({ + 'testResults': [{ + 'name': + ('invocations/task-chromium-swarm.appspot.com-6139bb/' + 'tests/ninja:%2F%2F:blink_web_tests%2Fshould-pass.html/' + 'results/033e-aaaa'), + 'testId': + 'ninja://:blink_web_tests/should-pass.html', + 'status': + 'FAIL', + }, { + 'name': + ('invocations/task-chromium-swarm.appspot.com-6139bb/' + 'tests/ninja:%2F%2F:blink_web_tests%2Fshould-pass.html/' + 'results/033e-bbbb'), + 'testId': + 'ninja://:blink_web_tests/should-pass.html', + 'status': + 'PASS', + 'expected': + True, + }, { + 'name': ('invocations/task-chromium-swarm.appspot.com-6139bb/' + 'tests/ninja:%2F%2F:blink_wpt_tests%2F' + 'external%2Fwpt%2Ftimeout.html/' + 'results/033e-cccc'), + 'testId': + 'ninja://:blink_wpt_tests/external/wpt/timeout.html', + 'status': + 'ABORT', + 'expected': + True, + }], + }) + self.fetcher.web.append_prpc_response({ + 'artifacts': [{ + 'name': + ('invocations/task-chromium-swarm.appspot.com-6139bb/' + 'tests/ninja:%2F%2F:blink_web_tests%2Fshould-pass.html/' + 'results/033e-aaaa/' + 'artifacts/actual_text'), + 'artifactId': + 'actual_text', + 'fetchUrl': + 'https://results.usercontent.cr.dev/actual_text', + }], + }) + results = self.fetcher.gather_results(Build('linux-rel', 9000, '1234'), + 'blink_web_tests (with patch)') + + result = results.result_for_test('should-pass.html') + self.assertEqual(result.actual_results(), ['FAIL', 'PASS']) + self.assertFalse(result.did_run_as_expected()) + self.assertEqual(result.result_dict()['artifacts'], { + 'actual_text': [ + 'https://results.usercontent.cr.dev/actual_text', + ], + }) + + result = results.result_for_test('external/wpt/timeout.html') + self.assertEqual(result.actual_results(), ['TIMEOUT']) + self.assertTrue(result.did_run_as_expected()) + def test_fetch_wpt_report_urls(self): self.fetcher.web.append_prpc_response({ 'artifacts': [{
diff --git a/third_party/blink/tools/blinkpy/tool/commands/rebaseline_cl_unittest.py b/third_party/blink/tools/blinkpy/tool/commands/rebaseline_cl_unittest.py index 8f037c5..de54841 100644 --- a/third_party/blink/tools/blinkpy/tool/commands/rebaseline_cl_unittest.py +++ b/third_party/blink/tools/blinkpy/tool/commands/rebaseline_cl_unittest.py
@@ -155,77 +155,6 @@ }, }, } - # TODO(crbug.com/1213998): Fix the example web test result format. - raw_test_results = [{ - "name": - "invocations/task-chromium-swarm.appspot.com-2/tests/ninja:%2F%2F:blink_web_tests%2Ftwo%2Fimage-fail.html", - "testId": "ninja://:blink_web_tests/two/image-fail.html", - "resultId": "2", - "variant": { - "def": { - "builder": "", - "os": "", - "test_suite": "blink_web_tests" - } - }, - "status": "FAIL" - }, { - "name": - "invocations/task-chromium-swarm.appspot.com-1/tests/ninja:%2F%2F:blink_wpt_tests%2Fone%2Fmissing.html", - "testId": "ninja://:blink_wpt_tests/one/missing.html", - "resultId": "1", - "variant": { - "def": { - "builder": "", - "os": "", - "test_suite": "blink_web_tests" - } - }, - "status": "FAIL" - }, { - "name": - "invocations/task-chromium-swarm.appspot.com-2/tests/ninja:%2F%2F:blink_web_tests%2Fone%2Fcrash.html", - "testId": "ninja://:blink_web_tests/one/crash.html", - "resultId": "3", - "variant": { - "def": { - "builder": "", - "os": "", - "test_suite": "blink_web_tests" - } - }, - "status": "CRASH" - }] - raw_artifacts = [{ - "name": - "invocations/task-chromium-swarm.appspot.com-1/tests/ninja:%2F%2F:blink_wpt_tests%2Fone%2Fmissing.html/results/1", - "artifactId": "actual_image", - "fetchUrl": - "https://results.usercontent.cr.dev/invocations/task-chromium-swarm.appspot.com-1/tests/ninja:%2F%2F:blink_wpt_tests%2Fone%2Fmissing.html/results/artifacts/actual_image?token=1", - "contentType": "image/png", - }, { - "name": - "invocations/task-chromium-swarm.appspot.com-2/tests/ninja:%2F%2F:blink_web_tests%2Ftwo%2Fimage-fail.html/results/2", - "artifactId": "actual_image", - "fetchUrl": - "https://results.usercontent.cr.dev/invocations/task-chromium-swarm.appspot.com-2/tests/ninja:%2F%2F:blink_web_tests%2Ftwo%2Fimage-fail.html/results/artifacts/actual_image?token=2", - "contentType": "image/png", - }, { - "name": - "invocations/task-chromium-swarm.appspot.com-2/tests/ninja:%2F%2F:blink_web_tests%2Fone%2Fcrash.html/results/3", - "artifactId": "actual_text", - "fetchUrl": - "https://results.usercontent.cr.dev/invocations/task-chromium-swarm.appspot.com-2/tests/ninja:%2F%2F:blink_web_tests%2Fone%2Fcrash.html/results/artifacts/actual_text?token=3", - "contentType": "text", - }] - # TODO(crbug.com/1376646): Need to test the ResultDB flag-specific path. - # Ideally, we would run all the same tests on both the ResultDB-enabled - # and ResultDB-disabled paths, only changing what `WebTestResults` are - # returned. - self.web_test_resultdb = self.tool.results_fetcher.make_results_from_raw_rdb( - raw_test_results, - raw_artifacts, - step_name='blink_web_tests (with patch)') for build in self.builds: self.tool.results_fetcher.set_results( @@ -327,21 +256,6 @@ self.assertEqual(self.tool.filesystem.files, files_before) self.assertEqual(self.tool.git().added_paths, set()) - def test_execute_basic_resultDB(self): - # By default, with no arguments or options, rebaseline-cl rebaselines - # all of the tests that unexpectedly failed. - for build in self.builds: - self.tool.results_fetcher.set_results(build, - self.web_test_resultdb) - exit_code = self.command.execute(self.command_options(resultDB=True), - [], self.tool) - self.assertEqual(exit_code, 0) - self.assertLog([ - 'INFO: All builds finished.\n', - 'INFO: Rebaselining one/missing.html\n', - 'INFO: Rebaselining two/image-fail.html\n', - ]) - def test_execute_with_test_name_file(self): fs = self.mac_port.host.filesystem test_name_file = fs.mktemp() @@ -369,34 +283,6 @@ 'INFO: Rebaselining two/image-fail.html\n', ]) - def test_execute_with_test_name_file_resultDB(self): - fs = self.mac_port.host.filesystem - test_name_file = fs.mktemp() - fs.write_text_file( - test_name_file, - textwrap.dedent(''' - one/missing.html - two/missing.html - # one/slow-fail.html - # - - ones/does-not-exist.html - two/image-fail.html ''')) - for build in self.builds: - self.tool.results_fetcher.set_results(build, - self.web_test_resultdb) - exit_code = self.command.execute( - self.command_options(test_name_file=test_name_file, resultDB=True), - [], self.tool) - self.assertEqual(exit_code, 0) - self.assertLog([ - 'INFO: All builds finished.\n', - 'INFO: Reading list of tests to rebaseline from %s\n' % - test_name_file, - 'INFO: Rebaselining one/missing.html\n', - 'INFO: Rebaselining two/image-fail.html\n', - ]) - def test_execute_with_no_issue_number_aborts(self): # If the user hasn't uploaded a CL, an error message is printed. self.command.git_cl = MockGitCL(self.tool, issue_number='None')
diff --git a/third_party/blink/web_tests/css3/flexbox/flex-order.html b/third_party/blink/web_tests/css3/flexbox/flex-order.html index b6bf574..e2969813 100644 --- a/third_party/blink/web_tests/css3/flexbox/flex-order.html +++ b/third_party/blink/web_tests/css3/flexbox/flex-order.html
@@ -83,8 +83,7 @@ <div class="third" style=""></div> </div> -<!-- Values greater than what can be stored in an int are clamped from - -2,147,483,646 (int min + 2) to 2,147,483,647. --> +<!-- Values greater than what can be stored in an int are clamped from -2,147,483,648 to 2,147,483,647. --> <div class="horizontal-box"> <div class="third" style="order: 4000000000"></div> <div class="fourth" style="order: 3000000000"></div> @@ -101,15 +100,15 @@ <div class="horizontal-box"> <div class="third" style="order: -2147483645"></div> - <div class="first" style="order: -2147483646"></div> - <div class="second" style="order: -2147483647"></div> + <div class="second" style="order: -2147483646"></div> + <div class="first" style="order: -2147483647"></div> <div class="fourth" style=""></div> </div> <div class="horizontal-box"> <div class="third" style="order: calc(-2147483640 - 5)"></div> - <div class="first" style="order: calc(-2147483640 - 6)"></div> - <div class="second" style="order: calc(-2147483640 - 7)"></div> + <div class="second" style="order: calc(-2147483640 - 6)"></div> + <div class="first" style="order: calc(-2147483640 - 7)"></div> <div class="fourth" style=""></div> </div>
diff --git a/third_party/blink/web_tests/wpt_internal/webgpu/fast_api/GPUCommandEncoder.writeTimestamp.https.html b/third_party/blink/web_tests/wpt_internal/webgpu/fast_api/GPUCommandEncoder.writeTimestamp.https.html new file mode 100644 index 0000000..23fa5b7 --- /dev/null +++ b/third_party/blink/web_tests/wpt_internal/webgpu/fast_api/GPUCommandEncoder.writeTimestamp.https.html
@@ -0,0 +1,76 @@ +<!DOCTYPE html> +<html> +<head> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +</head> +<body> +<script> +function optimizedMethodCall(commandEncoder, querySet, queryIndex) { + commandEncoder.writeTimestamp(querySet, queryIndex); +} + +function test(t, hotLoop, commandEncoder, querySet, queryIndex) { + try { + hotLoop(1, commandEncoder, querySet, queryIndex); + } catch(e) { + assert_true(e instanceof TypeError); + return; + } + assert_unreached("A TypeError should be thrown."); +} + +promise_test(async t => { + const adapter1 = await navigator.gpu.requestAdapter(); + assert_true(adapter1 instanceof GPUAdapter, 'Failed to request WebGPU adapter'); + + if (adapter1.features.has("timestamp-query")) { + return; + } + const deviceWithExtension = await adapter1.requestDevice({ + requiredFeatures: ["timestamp-query"], + }); + assert_true( + deviceWithExtension instanceof GPUDevice, + 'Failed to request WebGPU device with timestamp-query extension'); + + const timestampQuerySet = deviceWithExtension.createQuerySet({ + type: "timestamp", + count: 4, + }); + const encoder1 = deviceWithExtension.createCommandEncoder(); + function hotLoop(count, commandEncoder, querySet, queryIndex) { + for (let i = 0; i < count; ++i) { + optimizedMethodCall(commandEncoder, querySet, queryIndex); + } + } + hotLoop(100, encoder1, timestampQuerySet, 2); + + // Wait a bit for V8 to optimize. Then call again with an out-of-bounds value. + // An exception should be thrown. + await new Promise(resolve => t.step_timeout(resolve, 50)); + test(t, hotLoop, encoder1, timestampQuerySet, 0xFFFFFFFF + 1); + + const adapter2 = await navigator.gpu.requestAdapter(); + assert_true(adapter2 instanceof GPUAdapter, 'Failed to request WebGPU adapter'); + + const deviceWithoutExtension = await adapter2.requestDevice(); + assert_true(deviceWithoutExtension instanceof GPUDevice, 'Failed to request WebGPU device'); + + // We cannot create a timestamp query set without the extension "timestamp-query" enabled. As we + // only test the Blink-side validation and won't submit the command buffer to the server side, we + // can use an occlusion query set instead. + const occlusionQuerySet = deviceWithoutExtension.createQuerySet({ + type: "occlusion", + count: 4, + }); + const encoder2 = deviceWithoutExtension.createCommandEncoder(); + + // The TypeError caused by calling writeTimestamp() without enabling "timestamp-query" should + // still be thrown. + test(t, hotLoop, encoder2, occlusionQuerySet, 2); + test(t, hotLoop, encoder2, occlusionQuerySet, 0xFFFFFFFF + 1); +}); +</script> +</body> +</html>
diff --git a/third_party/closure_compiler/externs/file_manager_private.js b/third_party/closure_compiler/externs/file_manager_private.js index 24f3ae7..17720a9f 100644 --- a/third_party/closure_compiler/externs/file_manager_private.js +++ b/third_party/closure_compiler/externs/file_manager_private.js
@@ -868,7 +868,8 @@ * showNotification: boolean, * errorName: string, * pauseParams: (!chrome.fileManagerPrivate.PauseParams|undefined), - * outputs: (!Array<Entry>|undefined) + * outputs: (!Array<Entry>|undefined), + * destinationVolumeId: string * }} */ chrome.fileManagerPrivate.ProgressStatus;
diff --git a/third_party/closure_compiler/externs/runtime.js b/third_party/closure_compiler/externs/runtime.js index bc5666d..60aa82b0 100644 --- a/third_party/closure_compiler/externs/runtime.js +++ b/third_party/closure_compiler/externs/runtime.js
@@ -188,6 +188,23 @@ chrome.runtime.ExtensionContext; /** + * A filter to match against certain extension contexts. Matching contexts must match all specified filters; any filter that is not specified matches all available contexts. Thus, a filter of `{}` will match all available contexts. + * @typedef {{ + * contextTypes: (!Array<!chrome.runtime.ContextType>|undefined), + * contextIds: (!Array<string>|undefined), + * tabIds: (!Array<number>|undefined), + * windowIds: (!Array<number>|undefined), + * documentIds: (!Array<string>|undefined), + * frameIds: (!Array<number>|undefined), + * documentUrls: (!Array<string>|undefined), + * documentOrigins: (!Array<string>|undefined), + * incognito: (boolean|undefined) + * }} + * @see https://developer.chrome.com/extensions/runtime#type-ContextFilter + */ +chrome.runtime.ContextFilter; + +/** * This will be defined during an API method callback if there was an error * @typedef {{ * message: (string|undefined) @@ -393,11 +410,14 @@ /** * Fetches information about active contexts associated with this extension + * @param {!chrome.runtime.ContextFilter} filter A filter to find matching + * contexts. A context matches if it matches all specified fields in the + * filter. Any unspecified field in the filter matches all contexts. * @param {function(!Array<!chrome.runtime.ExtensionContext>): void} callback * Invoked with the matching contexts, if any. * @see https://developer.chrome.com/extensions/runtime#method-getContexts */ -chrome.runtime.getContexts = function(callback) {}; +chrome.runtime.getContexts = function(filter, callback) {}; /** * Fired when a profile that has this extension installed first starts up. This
diff --git a/third_party/libei/3pp/3pp.pb b/third_party/libei/3pp/3pp.pb index 77c7d7d..8b2952ac1 100644 --- a/third_party/libei/3pp/3pp.pb +++ b/third_party/libei/3pp/3pp.pb
@@ -1,11 +1,9 @@ create { platform_re: "linux-.*" - source { - url { - download_url: "https://gitlab.freedesktop.org/libinput/libei/-/archive/0.4.1/libei-0.4.1.tar.gz" - version: "0.4.1" - } - unpack_archive: true + source { script { name: "fetch.py" } } + build { + dep: "chromium/third_party/dbus" + install: "install.sh" } }
diff --git a/third_party/libei/3pp/fetch.py b/third_party/libei/3pp/fetch.py new file mode 100755 index 0000000..710d4f2c --- /dev/null +++ b/third_party/libei/3pp/fetch.py
@@ -0,0 +1,55 @@ +#!/usr/bin/env python3 +# 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. + +import argparse +import json +import os + +def do_latest(*args, **kwargs): + print('main') + + +def get_download_url(*args, **kwargs): + urls = [ + "https://gitlab.freedesktop.org/libinput/libei/-/" + "archive/main/libei-main.tar.gz", + "https://files.pythonhosted.org/packages/95/" + "7e/68018b70268fb4a2a605e2be44ab7b4dd7ce7808adae6c5ef32e34f4b55a/" + "MarkupSafe-2.1.2.tar.gz", + "https://github.com/WayneD/rsync/archive/refs/tags/v3.2.7.tar.gz", + "https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.4.114.tar.gz", + ] + + packages = [ + "libei-main.tar.gz", + "MarkupSafe-2.1.2.tar.gz", + "v3.2.7.tar.gz", # rsync tar ball + "linux-5.4.114.tar.gz", + ] + + partial_manifest = { + 'url': urls, + 'name': packages, + 'ext': '.tar.gz', + } + print(json.dumps(partial_manifest)) + + +def main(): + ap = argparse.ArgumentParser() + sub = ap.add_subparsers() + + latest_parser = sub.add_parser("latest") + latest_parser.set_defaults(func=do_latest) + + download_parser = sub.add_parser("get_url") + download_parser.set_defaults(func=get_download_url) + + opts = ap.parse_args() + opts.func(opts) + + +if __name__ == '__main__': + main()
diff --git a/third_party/libei/3pp/install.sh b/third_party/libei/3pp/install.sh index 62b6804..ec6b57d 100755 --- a/third_party/libei/3pp/install.sh +++ b/third_party/libei/3pp/install.sh
@@ -5,28 +5,82 @@ set -euxo pipefail +echo "Building on the following system: " +uname -a +uname -r + +SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) + PREFIX="$1" DEPS_PREFIX="$2" PWD="$(pwd)" -mkdir -p meson -MESON_PATH="$PWD/meson" +mkdir -p helper_pkgs +HELPER_PKGS_PATH="$PWD/helper_pkgs" # libei can be only build using Meson. We need Meson only to build libei # so it doesn't have to be uploaded to CIPD. -export PYTHONPATH="$MESON_PATH/lib/python3.6/site-packages" +export PYTHONPATH="$HELPER_PKGS_PATH/lib64/python3.6/site-packages:$HELPER_PKGS_PATH/lib/python3.6/site-packages/" + +echo "Installing dependencies to build libei" # Meson 0.62 and newer will require Python 3.7 and newer and Ubuntu 18.04 has # only Python 3.6 -python3 -m pip install meson==0.61.5 --prefix="$MESON_PATH" +python3 -m pip install meson==0.61.5 --prefix="$HELPER_PKGS_PATH" export PKG_CONFIG_PATH="$DEPS_PREFIX/lib64/pkgconfig" +export PATH="$PATH:$HELPER_PKGS_PATH:$HELPER_PKGS_PATH/bin" -$MESON_PATH/bin/meson setup build . --default-library=static -$MESON_PATH/bin/meson --buildtype=plain --prefix="$PREFIX" . build -D docs=disabled -D man=disabled -$MESON_PATH/bin/meson compile -C build --verbose -$MESON_PATH/bin/meson install -C build --no-rebuild +# Install required dependencies +python3 -m pip install --upgrade pip --prefix="$HELPER_PKGS_PATH" +python3 -m pip install cmake --prefer-binary --prefix="$HELPER_PKGS_PATH" +python3 -m pip install python-dbusmock --prefix="$HELPER_PKGS_PATH" +python3 -m pip install attr --prefix="$HELPER_PKGS_PATH" +python3 -m pip install pytest -v --prefer-binary --prefix="$HELPER_PKGS_PATH" 2>&1 +python3 -m pip install structlog --prefix="$HELPER_PKGS_PATH" +python3 -m pip install jinja2 --prefix="$HELPER_PKGS_PATH" + +echo "Installing MarkupSafe (a dependency for jinja2)" +tar xf MarkupSafe-2.1.2.tar.gz +cd MarkupSafe-2.1.2 +python3 setup.py install --prefix="$HELPER_PKGS_PATH" +cd .. + +echo "Installing rsync (needed by linux headers)" +tar xf v3.2.7.tar.gz +cd rsync-3.2.7 +./configure --prefix=${HELPER_PKGS_PATH} \ + --disable-md2man \ + --disable-openssl \ + --disable-xxhash \ + --disable-zstd \ + --disable-lz4 +make install -j$(nproc) +cd .. + +echo "Installing linux headers (libei depends on input-event-code.h)" +tar xf linux-5.4.114.tar.gz +cd linux-5.4.114 +# make headers_install INSTALL_HDR_PATH=/usr +make headers_install INSTALL_HDR_PATH=$PREFIX +cd .. + +echo "Building libei" +tar xf libei-main.tar.gz +cd libei-main +patch -p1 < ${SCRIPT_DIR}/patches/0001-config-Make-memfd_create-optional.patch + +echo "Compiler details (including search paths):" +`gcc -print-prog-name=cpp` -v + +$HELPER_PKGS_PATH/bin/meson setup -D c_args="-I$PREFIX/include" \ + -D c_link_args="-lm" build . --default-library=static \ + --prefix=$PREFIX --includedir=include +$HELPER_PKGS_PATH/bin/meson --buildtype=plain --prefix="$PREFIX" . build \ + -D docs=disabled -D man=disabled +$HELPER_PKGS_PATH/bin/meson compile -C build --verbose +$HELPER_PKGS_PATH/bin/meson install -C build --no-rebuild # We need pkgconfig file to point to location where it's going to be deployed # and not where it was installed -sed "s@$PREFIX@$DEPS_PREFIX@" -i "$PREFIX/lib64/pkgconfig/libei-0.4.1.pc" +sed "s@$PREFIX@$DEPS_PREFIX@" -i "$PREFIX/lib64/pkgconfig/libei.pc"
diff --git a/third_party/libei/3pp/patches/0001-config-Make-memfd_create-optional.patch b/third_party/libei/3pp/patches/0001-config-Make-memfd_create-optional.patch new file mode 100644 index 0000000..de7c328b --- /dev/null +++ b/third_party/libei/3pp/patches/0001-config-Make-memfd_create-optional.patch
@@ -0,0 +1,125 @@ +From 6b59355bac02d02048dc0cff5ec28f196467aea3 Mon Sep 17 00:00:00 2001 +From: Salman Malik <salmanmalik@chromium.org> +Date: Sat, 25 Mar 2023 23:22:01 +0000 +Subject: [PATCH] config: Make memfd_create optional + +`memfd_create` doesn't seem to be supported on +all platforms (e.g. ubuntu 18 has trouble with it). +Even though, I was able to substitute `memfd_create` +with a direct system call, I was not able to get +the `MFD_CLOXEC` flag (from fcntl.h) working cleanly +(there were redefinitions/conflicts for other +structures when trying to use <linux/*> headers). +Making it optional for time being till we have +figured out how to make it work broadly. +--- + meson.build | 3 +++ + src/util-memfile.c | 7 ++++++- + src/util-memfile.h | 2 ++ + test/test-ei-device.c | 2 ++ + tools/eis-demo-server.c | 2 ++ + 5 files changed, 15 insertions(+), 1 deletion(-) + +diff --git a/meson.build b/meson.build +index 61d78f8..63bafc8 100644 +--- a/meson.build ++++ b/meson.build +@@ -51,6 +51,9 @@ config_h = configuration_data() + config_h.set('_GNU_SOURCE', '1') + config_h.set_quoted('EI_VERSION', meson.project_version()) + config_h.set_quoted('EIS_VERSION', meson.project_version()) ++if cc.has_function('memfd_create', prefix: '#define _GNU_SOURCE\n#include <sys/mman.h>') ++ config_h.set('HAVE_MEMFD_CREATE', true) ++endif + + dep_libxkbcommon = dependency('xkbcommon', required: false) + config_h.set10('HAVE_LIBXKBCOMMON', dep_libxkbcommon.found()) +diff --git a/src/util-memfile.c b/src/util-memfile.c +index c4b8270..f74f7ec 100644 +--- a/src/util-memfile.c ++++ b/src/util-memfile.c +@@ -26,9 +26,12 @@ + + #include <stddef.h> + #include <unistd.h> +-#include <sys/mman.h> + #include <fcntl.h> + ++#ifdef HAVE_MEMFD_CREATE ++#include <sys/mman.h> ++#endif ++ + #include "util-memfile.h" + #include "util-mem.h" + #include "util-io.h" +@@ -55,6 +58,7 @@ OBJECT_IMPLEMENT_REF(memfile); + OBJECT_IMPLEMENT_GETTER(memfile, fd, int); + OBJECT_IMPLEMENT_GETTER(memfile, size, size_t); + ++#ifdef HAVE_MEMFD_CREATE + struct memfile * + memfile_new(const char *data, size_t sz) { + _unref_(memfile) *memfile = memfile_create(NULL); +@@ -81,3 +85,4 @@ memfile_new(const char *data, size_t sz) { + + return steal(&memfile); + } ++#endif +diff --git a/src/util-memfile.h b/src/util-memfile.h +index 66bdc28..51673f9 100644 +--- a/src/util-memfile.h ++++ b/src/util-memfile.h +@@ -28,8 +28,10 @@ + + struct memfile; + ++#ifdef HAVE_MEMFD_CREATE + struct memfile * + memfile_new(const char *data, size_t sz); ++#endif + + struct memfile * + memfile_ref(struct memfile *memfile); +diff --git a/test/test-ei-device.c b/test/test-ei-device.c +index ae7e698..529cc68 100644 +--- a/test/test-ei-device.c ++++ b/test/test-ei-device.c +@@ -1015,6 +1015,7 @@ MUNIT_TEST(test_ei_device_multitouch) + return MUNIT_OK; + } + ++#ifdef HAVE_MEMFD_CREATE + MUNIT_TEST(test_ei_keymap_invalid) + { + _unref_(peck) *peck = peck_new(); +@@ -1121,6 +1122,7 @@ MUNIT_TEST(test_ei_keymap_set) + + return MUNIT_OK; + } ++#endif + + MUNIT_TEST(test_ei_keyboard_modifiers) + { +diff --git a/tools/eis-demo-server.c b/tools/eis-demo-server.c +index 14195d6..b2a3acc 100644 +--- a/tools/eis-demo-server.c ++++ b/tools/eis-demo-server.c +@@ -207,6 +207,7 @@ setup_keymap(struct eis_demo_server *server, struct eis_device *device) + const char *str = xkb_keymap_get_as_string(keymap, XKB_KEYMAP_FORMAT_TEXT_V1); + size_t len = strlen(str) - 1; + ++#ifdef HAVE_MEMFD_CREATE + struct memfile *f = memfile_new(str, len); + if (!f) + return; +@@ -216,6 +217,7 @@ setup_keymap(struct eis_demo_server *server, struct eis_device *device) + memfile_get_size(f)); + eis_keymap_add(k); + memfile_unref(f); ++#endif + + _unref_(xkb_state) *state = xkb_state_new(keymap); + if (!state) +-- +2.40.0.348.gf938b09366-goog +
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/polymer_bundled.js b/third_party/polymer/v3_0/components-chromium/polymer/polymer_bundled.js index 02e83e5..2e3e246 100644 --- a/third_party/polymer/v3_0/components-chromium/polymer/polymer_bundled.js +++ b/third_party/polymer/v3_0/components-chromium/polymer/polymer_bundled.js
@@ -1732,18 +1732,18 @@ var gestures$1 = /*#__PURE__*/Object.freeze({ __proto__: null, - gestures: gestures, - recognizers: recognizers, - deepTargetFind: deepTargetFind, - addListener: addListener, - removeListener: removeListener, - register: register$1, - setTouchAction: setTouchAction, - prevent: prevent, - resetMouseCanceller: resetMouseCanceller, - findOriginalTarget: findOriginalTarget, add: add, - remove: remove + addListener: addListener, + deepTargetFind: deepTargetFind, + findOriginalTarget: findOriginalTarget, + gestures: gestures, + prevent: prevent, + recognizers: recognizers, + register: register$1, + remove: remove, + removeListener: removeListener, + resetMouseCanceller: resetMouseCanceller, + setTouchAction: setTouchAction }); /**
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/polymer_bundled.min.js b/third_party/polymer/v3_0/components-chromium/polymer/polymer_bundled.min.js index a1b2bf3..2add26d 100644 --- a/third_party/polymer/v3_0/components-chromium/polymer/polymer_bundled.min.js +++ b/third_party/polymer/v3_0/components-chromium/polymer/polymer_bundled.min.js
@@ -1 +1 @@ -window.JSCompiler_renameProperty=function(t,e){return t};let t=0,e=0,n=[],r=0,s=!1,i=document.createTextNode("");new window.MutationObserver((function(){s=!1;const t=n.length;for(let e=0;e<t;e++){let t=n[e];if(t)try{t()}catch(t){setTimeout((()=>{throw t}))}}n.splice(0,t),e+=t})).observe(i,{characterData:!0});const o={after:t=>({run:e=>window.setTimeout(e,t),cancel(t){window.clearTimeout(t)}}),run:(t,e)=>window.setTimeout(t,e),cancel(t){window.clearTimeout(t)}},a={run:t=>window.requestAnimationFrame(t),cancel(t){window.cancelAnimationFrame(t)}},l={run:t=>window.requestIdleCallback?window.requestIdleCallback(t):window.setTimeout(t,16),cancel(t){window.cancelIdleCallback?window.cancelIdleCallback(t):window.clearTimeout(t)}},h={run:e=>(s||(s=!0,i.textContent=r++),n.push(e),t++),cancel(t){const r=t-e;if(r>=0){if(!n[r])throw new Error("invalid async handle: "+t);n[r]=null}}};let c=0;const d=function(t){let e=t.__mixinApplications;e||(e=new WeakMap,t.__mixinApplications=e);let n=c++;return function(r){let s=r.__mixinSet;if(s&&s[n])return r;let i=e,o=i.get(r);if(!o){o=t(r),i.set(r,o);let e=Object.create(o.__mixinSet||s||null);e[n]=!0,o.__mixinSet=e}return o}};class _{constructor(){this._asyncModule=null,this._callback=null,this._timer=null}setConfig(t,e){this._asyncModule=t,this._callback=e,this._timer=this._asyncModule.run((()=>{this._timer=null,u.delete(this),this._callback()}))}cancel(){this.isActive()&&(this._cancelAsync(),u.delete(this))}_cancelAsync(){this.isActive()&&(this._asyncModule.cancel(this._timer),this._timer=null)}flush(){this.isActive()&&(this.cancel(),this._callback())}isActive(){return null!=this._timer}static debounce(t,e,n){return t instanceof _?t._cancelAsync():t=new _,t.setConfig(e,n),t}}let u=new Set;const p=function(t){u.add(t)},f=function(){const t=Boolean(u.size);return u.forEach((t=>{try{t.flush()}catch(t){setTimeout((()=>{throw t}))}})),t};let m,y,g=/(url\()([^)]*)(\))/g,b=/(^\/[^\/])|(^#)|(^[\w-\d]*:)/;function P(t,e){if(t&&b.test(t))return t;if("//"===t)return t;if(void 0===m){m=!1;try{const t=new URL("b","http://a");t.pathname="c%20d",m="http://a/c%20d"===t.href}catch(t){}}if(e||(e=document.baseURI||window.location.href),m)try{return new URL(t,e).href}catch(e){return t}return y||(y=document.implementation.createHTMLDocument("temp"),y.base=y.createElement("base"),y.head.appendChild(y.base),y.anchor=y.createElement("a"),y.body.appendChild(y.anchor)),y.base.href=e,y.anchor.href=t,y.anchor.href||t}function v(t,e){return t.replace(g,(function(t,n,r,s){return n+"'"+P(r.replace(/["']/g,""),e)+"'"+s}))}function C(t){return t.substring(0,t.lastIndexOf("/")+1)}const w=!0;Boolean(!0),"adoptedStyleSheets"in Document.prototype&&"replaceSync"in CSSStyleSheet.prototype&&(()=>{try{const t=new CSSStyleSheet;t.replaceSync("");const e=document.createElement("div");e.attachShadow({mode:"open"}),e.shadowRoot.adoptedStyleSheets=[t],e.shadowRoot.adoptedStyleSheets[0]}catch(t){return!1}})();let E=window.Polymer&&window.Polymer.rootPath||C(document.baseURI||window.location.href),T=window.Polymer&&window.Polymer.sanitizeDOMValue||void 0,O=window.Polymer&&window.Polymer.setPassiveTouchGestures||!1,A=window.Polymer&&window.Polymer.strictTemplatePolicy||!1,N=window.Polymer&&window.Polymer.allowTemplateFromDomModule||!1,x=(window.Polymer,!1),S=window.Polymer&&window.Polymer.orderedComputed||!1;const I=t=>t;let k="string"==typeof document.head.style.touchAction,L="__polymerGestures",M="__polymerGesturesHandled",D="__polymerGesturesTouchAction",R=["mousedown","mousemove","mouseup","click"],F=[0,1,4,2],H=function(){try{return 1===new MouseEvent("test",{buttons:1}).buttons}catch(t){return!1}}();function z(t){return R.indexOf(t)>-1}let j=!1;function B(t){if(!z(t)&&"touchend"!==t)return k&&j&&O?{passive:!0}:void 0}!function(){try{let t=Object.defineProperty({},"passive",{get(){j=!0}});window.addEventListener("test",null,t),window.removeEventListener("test",null,t)}catch(t){}}();let J=navigator.userAgent.match(/iP(?:[oa]d|hone)|Android/);const q=[],Y={button:!0,input:!0,keygen:!0,meter:!0,output:!0,textarea:!0,progress:!0,select:!0},U={button:!0,command:!0,fieldset:!0,input:!0,keygen:!0,optgroup:!0,option:!0,select:!0,textarea:!0};function $(t){let e=Array.prototype.slice.call(t.labels||[]);if(!e.length){e=[];try{let n=t.getRootNode();if(t.id){let r=n.querySelectorAll(`label[for = '${t.id}']`);for(let t=0;t<r.length;t++)e.push(r[t])}}catch(t){}}return e}let V=function(t){let e=t.sourceCapabilities;var n;if((!e||e.firesTouchEvents)&&(t[M]={skip:!0},"click"===t.type)){let e=!1,r=Q(t);for(let t=0;t<r.length;t++){if(r[t].nodeType===Node.ELEMENT_NODE)if("label"===r[t].localName)q.push(r[t]);else if(n=r[t],Y[n.localName]){let n=$(r[t]);for(let t=0;t<n.length;t++)e=e||q.indexOf(n[t])>-1}if(r[t]===W.mouse.target)return}if(e)return;t.preventDefault(),t.stopPropagation()}};function X(t){let e=J?["click"]:R;for(let n,r=0;r<e.length;r++)n=e[r],t?(q.length=0,document.addEventListener(n,V,!0)):document.removeEventListener(n,V,!0)}function G(t){let e=t.type;if(!z(e))return!1;if("mousemove"===e){let e=void 0===t.buttons?1:t.buttons;return t instanceof window.MouseEvent&&!H&&(e=F[t.which]||0),Boolean(1&e)}return 0===(void 0===t.button?0:t.button)}let W={mouse:{target:null,mouseIgnoreJob:null},touch:{x:0,y:0,id:-1,scrollDecided:!1}};function Z(t,e,n){t.movefn=e,t.upfn=n,document.addEventListener("mousemove",e),document.addEventListener("mouseup",n)}function K(t){document.removeEventListener("mousemove",t.movefn),document.removeEventListener("mouseup",t.upfn),t.movefn=null,t.upfn=null}document.addEventListener("touchend",(function(t){W.mouse.mouseIgnoreJob||X(!0),W.mouse.target=Q(t)[0],W.mouse.mouseIgnoreJob=_.debounce(W.mouse.mouseIgnoreJob,o.after(2500),(function(){X(),W.mouse.target=null,W.mouse.mouseIgnoreJob=null}))}),!!j&&{passive:!0});const Q=t=>t.composedPath&&t.composedPath()||[],tt={},et=[];function nt(t,e){let n=document.elementFromPoint(t,e),r=n;for(;r&&r.shadowRoot;){let s=r;if(r=r.shadowRoot.elementFromPoint(t,e),s===r)break;r&&(n=r)}return n}function rt(t){const e=Q(t);return e.length>0?e[0]:t.target}function st(t){let e,n=t.type,r=t.currentTarget.__polymerGestures;if(!r)return;let s=r[n];if(s){if(!t[M]&&(t[M]={},"touch"===n.slice(0,5))){let e=(t=t).changedTouches[0];if("touchstart"===n&&1===t.touches.length&&(W.touch.id=e.identifier),W.touch.id!==e.identifier)return;k||"touchstart"!==n&&"touchmove"!==n||function(t){let e=t.changedTouches[0],n=t.type;if("touchstart"===n)W.touch.x=e.clientX,W.touch.y=e.clientY,W.touch.scrollDecided=!1;else if("touchmove"===n){if(W.touch.scrollDecided)return;W.touch.scrollDecided=!0;let n=function(t){let e="auto",n=Q(t);for(let t,r=0;r<n.length;r++)if(t=n[r],t[D]){e=t[D];break}return e}(t),r=!1,s=Math.abs(W.touch.x-e.clientX),i=Math.abs(W.touch.y-e.clientY);t.cancelable&&("none"===n?r=!0:"pan-x"===n?r=i>s:"pan-y"===n&&(r=s>i)),r?t.preventDefault():ct("track")}}(t)}if(e=t[M],!e.skip){for(let n,r=0;r<et.length;r++)n=et[r],s[n.name]&&!e[n.name]&&n.flow&&n.flow.start.indexOf(t.type)>-1&&n.reset&&n.reset();for(let r,i=0;i<et.length;i++)r=et[i],s[r.name]&&!e[r.name]&&(e[r.name]=!0,r[n](t))}}}function it(t,e,n){return!!tt[e]&&(function(t,e,n){let r=tt[e],s=r.deps,i=r.name,o=t[L];o||(t[L]=o={});for(let e,n,r=0;r<s.length;r++)e=s[r],J&&z(e)&&"click"!==e||(n=o[e],n||(o[e]=n={_count:0}),0===n._count&&t.addEventListener(e,st,B(e)),n[i]=(n[i]||0)+1,n._count=(n._count||0)+1);t.addEventListener(e,n),r.touchAction&<(t,r.touchAction)}(t,e,n),!0)}function ot(t,e,n){return!!tt[e]&&(function(t,e,n){let r=tt[e],s=r.deps,i=r.name,o=t[L];if(o)for(let e,n,r=0;r<s.length;r++)e=s[r],n=o[e],n&&n[i]&&(n[i]=(n[i]||1)-1,n._count=(n._count||1)-1,0===n._count&&t.removeEventListener(e,st,B(e)));t.removeEventListener(e,n)}(t,e,n),!0)}function at(t){et.push(t);for(let e=0;e<t.emits.length;e++)tt[t.emits[e]]=t}function lt(t,e){k&&t instanceof HTMLElement&&h.run((()=>{t.style.touchAction=e})),t[D]=e}function ht(t,e,n){let r=new Event(e,{bubbles:!0,cancelable:!0,composed:!0});if(r.detail=n,I(t).dispatchEvent(r),r.defaultPrevented){let t=n.preventer||n.sourceEvent;t&&t.preventDefault&&t.preventDefault()}}function ct(t){let e=function(t){for(let e,n=0;n<et.length;n++){e=et[n];for(let n,r=0;r<e.emits.length;r++)if(n=e.emits[r],n===t)return e}return null}(t);e.info&&(e.info.prevent=!0)}function dt(t,e,n,r){e&&ht(e,t,{x:n.clientX,y:n.clientY,sourceEvent:n,preventer:r,prevent:function(t){return ct(t)}})}function _t(t,e,n){if(t.prevent)return!1;if(t.started)return!0;let r=Math.abs(t.x-e),s=Math.abs(t.y-n);return r>=5||s>=5}function ut(t,e,n){if(!e)return;let r,s=t.moves[t.moves.length-2],i=t.moves[t.moves.length-1],o=i.x-t.x,a=i.y-t.y,l=0;s&&(r=i.x-s.x,l=i.y-s.y),ht(e,"track",{state:t.state,x:n.clientX,y:n.clientY,dx:o,dy:a,ddx:r,ddy:l,sourceEvent:n,hover:function(){return nt(n.clientX,n.clientY)}})}function pt(t,e,n){let r=Math.abs(e.clientX-t.x),s=Math.abs(e.clientY-t.y),i=rt(n||e);!i||U[i.localName]&&i.hasAttribute("disabled")||(isNaN(r)||isNaN(s)||r<=25&&s<=25||function(t){if("click"===t.type){if(0===t.detail)return!0;let e=rt(t);if(!e.nodeType||e.nodeType!==Node.ELEMENT_NODE)return!0;let n=e.getBoundingClientRect(),r=t.pageX,s=t.pageY;return!(r>=n.left&&r<=n.right&&s>=n.top&&s<=n.bottom)}return!1}(e))&&(t.prevent||ht(i,"tap",{x:e.clientX,y:e.clientY,sourceEvent:e,preventer:n}))}at({name:"downup",deps:["mousedown","touchstart","touchend"],flow:{start:["mousedown","touchstart"],end:["mouseup","touchend"]},emits:["down","up"],info:{movefn:null,upfn:null},reset:function(){K(this.info)},mousedown:function(t){if(!G(t))return;let e=rt(t),n=this;Z(this.info,(function(t){G(t)||(dt("up",e,t),K(n.info))}),(function(t){G(t)&&dt("up",e,t),K(n.info)})),dt("down",e,t)},touchstart:function(t){dt("down",rt(t),t.changedTouches[0],t)},touchend:function(t){dt("up",rt(t),t.changedTouches[0],t)}}),at({name:"track",touchAction:"none",deps:["mousedown","touchstart","touchmove","touchend"],flow:{start:["mousedown","touchstart"],end:["mouseup","touchend"]},emits:["track"],info:{x:0,y:0,state:"start",started:!1,moves:[],addMove:function(t){this.moves.length>2&&this.moves.shift(),this.moves.push(t)},movefn:null,upfn:null,prevent:!1},reset:function(){this.info.state="start",this.info.started=!1,this.info.moves=[],this.info.x=0,this.info.y=0,this.info.prevent=!1,K(this.info)},mousedown:function(t){if(!G(t))return;let e=rt(t),n=this,r=function(t){let r=t.clientX,s=t.clientY;_t(n.info,r,s)&&(n.info.state=n.info.started?"mouseup"===t.type?"end":"track":"start","start"===n.info.state&&ct("tap"),n.info.addMove({x:r,y:s}),G(t)||(n.info.state="end",K(n.info)),e&&ut(n.info,e,t),n.info.started=!0)};Z(this.info,r,(function(t){n.info.started&&r(t),K(n.info)})),this.info.x=t.clientX,this.info.y=t.clientY},touchstart:function(t){let e=t.changedTouches[0];this.info.x=e.clientX,this.info.y=e.clientY},touchmove:function(t){let e=rt(t),n=t.changedTouches[0],r=n.clientX,s=n.clientY;_t(this.info,r,s)&&("start"===this.info.state&&ct("tap"),this.info.addMove({x:r,y:s}),ut(this.info,e,n),this.info.state="track",this.info.started=!0)},touchend:function(t){let e=rt(t),n=t.changedTouches[0];this.info.started&&(this.info.state="end",this.info.addMove({x:n.clientX,y:n.clientY}),ut(this.info,e,n))}}),at({name:"tap",deps:["mousedown","click","touchstart","touchend"],flow:{start:["mousedown","touchstart"],end:["click","touchend"]},emits:["tap"],info:{x:NaN,y:NaN,prevent:!1},reset:function(){this.info.x=NaN,this.info.y=NaN,this.info.prevent=!1},mousedown:function(t){G(t)&&(this.info.x=t.clientX,this.info.y=t.clientY)},click:function(t){G(t)&&pt(this.info,t)},touchstart:function(t){const e=t.changedTouches[0];this.info.x=e.clientX,this.info.y=e.clientY},touchend:function(t){pt(this.info,t.changedTouches[0],t)}});const ft=rt,mt=it,yt=ot;var gt=Object.freeze({__proto__:null,gestures:tt,recognizers:et,deepTargetFind:nt,addListener:it,removeListener:ot,register:at,setTouchAction:lt,prevent:ct,resetMouseCanceller:function(){W.mouse.mouseIgnoreJob&&W.mouse.mouseIgnoreJob.flush()},findOriginalTarget:ft,add:mt,remove:yt});let bt={},Pt={};function vt(t,e){bt[t]=Pt[t.toLowerCase()]=e}function Ct(t){return bt[t]||Pt[t.toLowerCase()]}class wt extends HTMLElement{static get observedAttributes(){return["id"]}static import(t,e){if(t){let n=Ct(t);return n&&e?n.querySelector(e):n}return null}attributeChangedCallback(t,e,n,r){e!==n&&this.register()}get assetpath(){if(!this.__assetpath){const t=window.HTMLImports&&HTMLImports.importForElement?HTMLImports.importForElement(this)||document:this.ownerDocument,e=P(this.getAttribute("assetpath")||"",t.baseURI);this.__assetpath=C(e)}return this.__assetpath}register(t){if(t=t||this.id){if(A&&void 0!==Ct(t))throw vt(t,null),new Error(`strictTemplatePolicy: dom-module ${t} re-registered`);this.id=t,vt(t,this),(e=this).querySelector("style")&&console.warn("dom-module %s has style outside template",e.id)}var e}}wt.prototype.modules=bt,customElements.define("dom-module",wt);function Et(t){return wt.import(t)}function Tt(t){const e=v((t.body?t.body:t).textContent,t.baseURI),n=document.createElement("style");return n.textContent=e,n}function Ot(t){const e=t.trim().split(/\s+/),n=[];for(let t=0;t<e.length;t++)n.push(...At(e[t]));return n}function At(t){const e=Et(t);if(!e)return console.warn("Could not find style data in module named",t),[];if(void 0===e._styles){const t=[];t.push(...xt(e));const n=e.querySelector("template");n&&t.push(...Nt(n,e.assetpath)),e._styles=t}return e._styles}function Nt(t,e){if(!t._styles){const n=[],r=t.content.querySelectorAll("style");for(let t=0;t<r.length;t++){let s=r[t],i=s.getAttribute("include");i&&n.push(...Ot(i).filter((function(t,e,n){return n.indexOf(t)===e}))),e&&(s.textContent=v(s.textContent,e)),n.push(s)}t._styles=n}return t._styles}function xt(t){const e=[],n=t.querySelectorAll("link[rel=import][type~=css]");for(let t=0;t<n.length;t++){let r=n[t];if(r.import){const t=r.import,n=r.hasAttribute("shady-unscoped");if(n&&!t._unscopedStyle){const e=Tt(t);e.setAttribute("shady-unscoped",""),t._unscopedStyle=e}else t._style||(t._style=Tt(t));e.push(n?t._unscopedStyle:t._style)}}return e}function St(t){let e=Et(t);if(e&&void 0===e._cssText){let t=function(t){let e="",n=xt(t);for(let t=0;t<n.length;t++)e+=n[t].textContent;return e}(e),n=e.querySelector("template");n&&(t+=function(t,e){let n="";const r=Nt(t,e);for(let t=0;t<r.length;t++){let e=r[t];e.parentNode&&e.parentNode.removeChild(e),n+=e.textContent}return n}(n,e.assetpath)),e._cssText=t||null}return e||console.warn("Could not find style data in module named",t),e&&e._cssText||""}function It(t){return t.indexOf(".")>=0}function kt(t){let e=t.indexOf(".");return-1===e?t:t.slice(0,e)}function Lt(t,e){return 0===t.indexOf(e+".")}function Mt(t,e){return 0===e.indexOf(t+".")}function Dt(t,e,n){return e+n.slice(t.length)}function Rt(t,e){return t===e||Lt(t,e)||Mt(t,e)}function Ft(t){if(Array.isArray(t)){let e=[];for(let n=0;n<t.length;n++){let r=t[n].toString().split(".");for(let t=0;t<r.length;t++)e.push(r[t])}return e.join(".")}return t}function Ht(t){return Array.isArray(t)?Ft(t).split("."):t.toString().split(".")}function zt(t,e,n){let r=t,s=Ht(e);for(let t=0;t<s.length;t++){if(!r)return;r=r[s[t]]}return n&&(n.path=s.join(".")),r}function jt(t,e,n){let r=t,s=Ht(e),i=s[s.length-1];if(s.length>1){for(let t=0;t<s.length-1;t++){if(r=r[s[t]],!r)return}r[i]=n}else r[e]=n;return s.join(".")}const Bt={},Jt=/-[a-z]/g,qt=/([A-Z])/g;function Yt(t){return Bt[t]||(Bt[t]=t.indexOf("-")<0?t:t.replace(Jt,(t=>t[1].toUpperCase())))}function Ut(t){return Bt[t]||(Bt[t]=t.replace(qt,"-$1").toLowerCase())}const $t=h,Vt=d((t=>class extends t{static createProperties(t){const e=this.prototype;for(let n in t)n in e||e._createPropertyAccessor(n)}static attributeNameForProperty(t){return t.toLowerCase()}static typeForProperty(t){}_createPropertyAccessor(t,e){this._addPropertyToAttributeMap(t),this.hasOwnProperty(JSCompiler_renameProperty("__dataHasAccessor",this))||(this.__dataHasAccessor=Object.assign({},this.__dataHasAccessor)),this.__dataHasAccessor[t]||(this.__dataHasAccessor[t]=!0,this._definePropertyAccessor(t,e))}_addPropertyToAttributeMap(t){this.hasOwnProperty(JSCompiler_renameProperty("__dataAttributes",this))||(this.__dataAttributes=Object.assign({},this.__dataAttributes));let e=this.__dataAttributes[t];return e||(e=this.constructor.attributeNameForProperty(t),this.__dataAttributes[e]=t),e}_definePropertyAccessor(t,e){Object.defineProperty(this,t,{get(){return this.__data[t]},set:e?function(){}:function(e){this._setPendingProperty(t,e,!0)&&this._invalidateProperties()}})}constructor(){super(),this.__dataEnabled=!1,this.__dataReady=!1,this.__dataInvalid=!1,this.__data={},this.__dataPending=null,this.__dataOld=null,this.__dataInstanceProps=null,this.__dataCounter=0,this.__serializing=!1,this._initializeProperties()}ready(){this.__dataReady=!0,this._flushProperties()}_initializeProperties(){for(let t in this.__dataHasAccessor)this.hasOwnProperty(t)&&(this.__dataInstanceProps=this.__dataInstanceProps||{},this.__dataInstanceProps[t]=this[t],delete this[t])}_initializeInstanceProperties(t){Object.assign(this,t)}_setProperty(t,e){this._setPendingProperty(t,e)&&this._invalidateProperties()}_getProperty(t){return this.__data[t]}_setPendingProperty(t,e,n){let r=this.__data[t],s=this._shouldPropertyChange(t,e,r);return s&&(this.__dataPending||(this.__dataPending={},this.__dataOld={}),this.__dataOld&&!(t in this.__dataOld)&&(this.__dataOld[t]=r),this.__data[t]=e,this.__dataPending[t]=e),s}_isPropertyPending(t){return!(!this.__dataPending||!this.__dataPending.hasOwnProperty(t))}_invalidateProperties(){!this.__dataInvalid&&this.__dataReady&&(this.__dataInvalid=!0,$t.run((()=>{this.__dataInvalid&&(this.__dataInvalid=!1,this._flushProperties())})))}_enableProperties(){this.__dataEnabled||(this.__dataEnabled=!0,this.__dataInstanceProps&&(this._initializeInstanceProperties(this.__dataInstanceProps),this.__dataInstanceProps=null),this.ready())}_flushProperties(){this.__dataCounter++;const t=this.__data,e=this.__dataPending,n=this.__dataOld;this._shouldPropertiesChange(t,e,n)&&(this.__dataPending=null,this.__dataOld=null,this._propertiesChanged(t,e,n)),this.__dataCounter--}_shouldPropertiesChange(t,e,n){return Boolean(e)}_propertiesChanged(t,e,n){}_shouldPropertyChange(t,e,n){return n!==e&&(n==n||e==e)}attributeChangedCallback(t,e,n,r){e!==n&&this._attributeToProperty(t,n),super.attributeChangedCallback&&super.attributeChangedCallback(t,e,n,r)}_attributeToProperty(t,e,n){if(!this.__serializing){const r=this.__dataAttributes,s=r&&r[t]||t;this[s]=this._deserializeValue(e,n||this.constructor.typeForProperty(s))}}_propertyToAttribute(t,e,n){this.__serializing=!0,n=arguments.length<3?this[t]:n,this._valueToNodeAttribute(this,n,e||this.constructor.attributeNameForProperty(t)),this.__serializing=!1}_valueToNodeAttribute(t,e,n){const r=this._serializeValue(e);"class"!==n&&"name"!==n&&"slot"!==n||(t=I(t)),void 0===r?t.removeAttribute(n):t.setAttribute(n,r)}_serializeValue(t){switch(typeof t){case"boolean":return t?"":void 0;default:return null!=t?t.toString():void 0}}_deserializeValue(t,e){switch(e){case Boolean:return null!==t;case Number:return Number(t);default:return t}}})),Xt={};let Gt=HTMLElement.prototype;for(;Gt;){let t=Object.getOwnPropertyNames(Gt);for(let e=0;e<t.length;e++)Xt[t[e]]=!0;Gt=Object.getPrototypeOf(Gt)}const Wt=window.trustedTypes?t=>trustedTypes.isHTML(t)||trustedTypes.isScript(t)||trustedTypes.isScriptURL(t):()=>!1;const Zt=d((t=>{const e=Vt(t);return class extends e{static createPropertiesForAttributes(){let t=this.observedAttributes;for(let e=0;e<t.length;e++)this.prototype._createPropertyAccessor(Yt(t[e]))}static attributeNameForProperty(t){return Ut(t)}_initializeProperties(){this.__dataProto&&(this._initializeProtoProperties(this.__dataProto),this.__dataProto=null),super._initializeProperties()}_initializeProtoProperties(t){for(let e in t)this._setProperty(e,t[e])}_ensureAttribute(t,e){const n=this;n.hasAttribute(t)||this._valueToNodeAttribute(n,e,t)}_serializeValue(t){switch(typeof t){case"object":if(t instanceof Date)return t.toString();if(t){if(Wt(t))return t;try{return JSON.stringify(t)}catch(t){return""}}default:return super._serializeValue(t)}}_deserializeValue(t,e){let n;switch(e){case Object:try{n=JSON.parse(t)}catch(e){n=t}break;case Array:try{n=JSON.parse(t)}catch(e){n=null,console.warn("Polymer::Attributes: couldn't decode Array as JSON: "+t)}break;case Date:n=isNaN(t)?String(t):Number(t),n=new Date(n);break;default:n=super._deserializeValue(t,e)}return n}_definePropertyAccessor(t,e){!function(t,e){if(!Xt[e]){let n=t[e];void 0!==n&&(t.__data?t._setPendingProperty(e,n):(t.__dataProto?t.hasOwnProperty(JSCompiler_renameProperty("__dataProto",t))||(t.__dataProto=Object.create(t.__dataProto)):t.__dataProto={},t.__dataProto[e]=n))}}(this,t),super._definePropertyAccessor(t,e)}_hasAccessor(t){return this.__dataHasAccessor&&this.__dataHasAccessor[t]}_isPropertyPending(t){return Boolean(this.__dataPending&&t in this.__dataPending)}}})),Kt={"dom-if":!0,"dom-repeat":!0};let Qt=!1,te=!1;function ee(t){(function(){if(!Qt){Qt=!0;const t=document.createElement("textarea");t.placeholder="a",te=t.placeholder===t.textContent}return te})()&&"textarea"===t.localName&&t.placeholder&&t.placeholder===t.textContent&&(t.textContent=null)}const ne=(()=>{const t=window.trustedTypes&&window.trustedTypes.createPolicy("polymer-template-event-attribute-policy",{createScript:t=>t});return(e,n,r)=>{const s=n.getAttribute(r);t&&r.startsWith("on-")?e.setAttribute(r,t.createScript(s,r)):e.setAttribute(r,s)}})();function re(t){let e=t.getAttribute("is");if(e&&Kt[e]){let n=t;for(n.removeAttribute("is"),t=n.ownerDocument.createElement(e),n.parentNode.replaceChild(t,n),t.appendChild(n);n.attributes.length;){const{name:e}=n.attributes[0];ne(t,n,e),n.removeAttribute(e)}}return t}function se(t,e){let n=e.parentInfo&&se(t,e.parentInfo);if(!n)return t;for(let t=n.firstChild,r=0;t;t=t.nextSibling)if(e.parentIndex===r++)return t}function ie(t,e,n,r){r.id&&(e[r.id]=n)}function oe(t,e,n){if(n.events&&n.events.length)for(let r,s=0,i=n.events;s<i.length&&(r=i[s]);s++)t._addMethodEventListenerToNode(e,r.name,r.value,t)}function ae(t,e,n,r){n.templateInfo&&(e._templateInfo=n.templateInfo,e._parentTemplateInfo=r)}const le=d((t=>class extends t{static _parseTemplate(t,e){if(!t._templateInfo){let n=t._templateInfo={};n.nodeInfoList=[],n.nestedTemplate=Boolean(e),n.stripWhiteSpace=!0,this._parseTemplateContent(t,n,{parent:null})}return t._templateInfo}static _parseTemplateContent(t,e,n){return this._parseTemplateNode(t.content,e,n)}static _parseTemplateNode(t,e,n){let r=!1,s=t;return"template"!=s.localName||s.hasAttribute("preserve-content")?"slot"===s.localName&&(e.hasInsertionPoint=!0):r=this._parseTemplateNestedTemplate(s,e,n)||r,ee(s),s.firstChild&&this._parseTemplateChildNodes(s,e,n),s.hasAttributes&&s.hasAttributes()&&(r=this._parseTemplateNodeAttributes(s,e,n)||r),r||n.noted}static _parseTemplateChildNodes(t,e,n){if("script"!==t.localName&&"style"!==t.localName)for(let r,s=t.firstChild,i=0;s;s=r){if("template"==s.localName&&(s=re(s)),r=s.nextSibling,s.nodeType===Node.TEXT_NODE){let n=r;for(;n&&n.nodeType===Node.TEXT_NODE;)s.textContent+=n.textContent,r=n.nextSibling,t.removeChild(n),n=r;if(e.stripWhiteSpace&&!s.textContent.trim()){t.removeChild(s);continue}}let o={parentIndex:i,parentInfo:n};this._parseTemplateNode(s,e,o)&&(o.infoIndex=e.nodeInfoList.push(o)-1),s.parentNode&&i++}}static _parseTemplateNestedTemplate(t,e,n){let r=t,s=this._parseTemplate(r,e);return(s.content=r.content.ownerDocument.createDocumentFragment()).appendChild(r.content),n.templateInfo=s,!0}static _parseTemplateNodeAttributes(t,e,n){let r=!1,s=Array.from(t.attributes);for(let i,o=s.length-1;i=s[o];o--)r=this._parseTemplateNodeAttribute(t,e,n,i.name,i.value)||r;return r}static _parseTemplateNodeAttribute(t,e,n,r,s){return"on-"===r.slice(0,3)?(t.removeAttribute(r),n.events=n.events||[],n.events.push({name:r.slice(3),value:s}),!0):"id"===r&&(n.id=s,!0)}static _contentForTemplate(t){let e=t._templateInfo;return e&&e.content||t.content}_stampTemplate(t,e){t&&!t.content&&window.HTMLTemplateElement&&HTMLTemplateElement.decorate&&HTMLTemplateElement.decorate(t);let n=(e=e||this.constructor._parseTemplate(t)).nodeInfoList,r=e.content||t.content,s=document.importNode(r,!0);s.__noInsertionPoint=!e.hasInsertionPoint;let i=s.nodeList=new Array(n.length);s.$={};for(let t,r=0,o=n.length;r<o&&(t=n[r]);r++){let n=i[r]=se(s,t);ie(0,s.$,n,t),ae(0,n,t,e),oe(this,n,t)}return s=s,s}_addMethodEventListenerToNode(t,e,n,r){let s=function(t,e,n){return t=t._methodHost||t,function(e){t[n]?t[n](e,e.detail):console.warn("listener method `"+n+"` not defined")}}(r=r||t,0,n);return this._addEventListenerToNode(t,e,s),s}_addEventListenerToNode(t,e,n){t.addEventListener(e,n)}_removeEventListenerFromNode(t,e,n){t.removeEventListener(e,n)}}));let he=0;const ce=[],de={COMPUTE:"__computeEffects",REFLECT:"__reflectEffects",NOTIFY:"__notifyEffects",PROPAGATE:"__propagateEffects",OBSERVE:"__observeEffects",READ_ONLY:"__readOnly"},_e=/[A-Z]/;function ue(t,e,n){let r=t[e];if(r){if(!t.hasOwnProperty(e)&&(r=t[e]=Object.create(t[e]),n))for(let t in r){let e=r[t],n=r[t]=Array(e.length);for(let t=0;t<e.length;t++)n[t]=e[t]}}else r=t[e]={};return r}function pe(t,e,n,r,s,i){if(e){let o=!1;const a=he++;for(let l in n){let h=e[s?kt(l):l];if(h)for(let e,c=0,d=h.length;c<d&&(e=h[c]);c++)e.info&&e.info.lastRun===a||s&&!me(l,e.trigger)||(e.info&&(e.info.lastRun=a),e.fn(t,l,n,r,e.info,s,i),o=!0)}return o}return!1}function fe(t,e,n,r,s,i,o,a){let l=!1,h=e[o?kt(r):r];if(h)for(let e,c=0,d=h.length;c<d&&(e=h[c]);c++)e.info&&e.info.lastRun===n||o&&!me(r,e.trigger)||(e.info&&(e.info.lastRun=n),e.fn(t,r,s,i,e.info,o,a),l=!0);return l}function me(t,e){if(e){let n=e.name;return n==t||!(!e.structured||!Lt(n,t))||!(!e.wildcard||!Mt(n,t))}return!0}function ye(t,e,n,r,s){let i="string"==typeof s.method?t[s.method]:s.method,o=s.property;i?i.call(t,t.__data[o],r[o]):s.dynamicFn||console.warn("observer method `"+s.method+"` not defined")}function ge(t,e,n){let r=kt(e);if(r!==e){return be(t,Ut(r)+"-changed",n[e],e),!0}return!1}function be(t,e,n,r){let s={value:n,queueProperty:!0};r&&(s.path=r),I(t).dispatchEvent(new CustomEvent(e,{detail:s}))}function Pe(t,e,n,r,s,i){let o=(i?kt(e):e)!=e?e:null,a=o?zt(t,o):t.__data[e];o&&void 0===a&&(a=n[e]),be(t,s.eventName,a,o)}function ve(t,e,n,r,s){let i=t.__data[e];T&&(i=T(i,s.attrName,"attribute",t)),t._propertyToAttribute(e,s.attrName,i)}function Ce(t,e,n,r){let s=t[de.COMPUTE];if(s)if(S){he++;const i=function(t){let e=t.constructor.__orderedComputedDeps;if(!e){e=new Map;const n=t[de.COMPUTE];let r,{counts:s,ready:i,total:o}=function(t){const e=t.__computeInfo,n={},r=t[de.COMPUTE],s=[];let i=0;for(let t in e){const r=e[t];i+=n[t]=r.args.filter((t=>!t.literal)).length+(r.dynamicFn?1:0)}for(let t in r)e[t]||s.push(t);return{counts:n,ready:s,total:i}}(t);for(;r=i.shift();){e.set(r,e.size);const t=n[r];t&&t.forEach((t=>{const e=t.info.methodInfo;--o,0==--s[e]&&i.push(e)}))}if(0!==o){const e=t;console.warn(`Computed graph for ${e.localName} incomplete; circular?`)}t.constructor.__orderedComputedDeps=e}return e}(t),o=[];for(let t in e)Ee(t,s,o,i,r);let a;for(;a=o.shift();)Te(t,"",e,n,a)&&Ee(a.methodInfo,s,o,i,r);Object.assign(n,t.__dataOld),Object.assign(e,t.__dataPending),t.__dataPending=null}else{let i=e;for(;pe(t,s,i,n,r);)Object.assign(n,t.__dataOld),Object.assign(e,t.__dataPending),i=t.__dataPending,t.__dataPending=null}}const we=(t,e,n)=>{let r=0,s=e.length-1,i=-1;for(;r<=s;){const o=r+s>>1,a=n.get(e[o].methodInfo)-n.get(t.methodInfo);if(a<0)r=o+1;else{if(!(a>0)){i=o;break}s=o-1}}i<0&&(i=s+1),e.splice(i,0,t)},Ee=(t,e,n,r,s)=>{const i=e[s?kt(t):t];if(i)for(let e=0;e<i.length;e++){const o=i[e];o.info.lastRun===he||s&&!me(t,o.trigger)||(o.info.lastRun=he,we(o.info,n,r))}};function Te(t,e,n,r,s){let i=ke(t,e,n,r,s);if(i===ce)return!1;let o=s.methodInfo;return t.__dataHasAccessor&&t.__dataHasAccessor[o]?t._setPendingProperty(o,i,!0):(t[o]=i,!1)}function Oe(t,e,n,r,s,i,o){n.bindings=n.bindings||[];let a={kind:r,target:s,parts:i,literal:o,isCompound:1!==i.length};if(n.bindings.push(a),function(t){return Boolean(t.target)&&"attribute"!=t.kind&&"text"!=t.kind&&!t.isCompound&&"{"===t.parts[0].mode}(a)){let{event:t,negate:e}=a.parts[0];a.listenerEvent=t||Ut(s)+"-changed",a.listenerNegate=e}let l=e.nodeInfoList.length;for(let n=0;n<a.parts.length;n++){let r=a.parts[n];r.compoundIndex=n,Ae(t,e,a,r,l)}}function Ae(t,e,n,r,s){if(!r.literal)if("attribute"===n.kind&&"-"===n.target[0])console.warn("Cannot set attribute "+n.target+' because "-" is not a valid attribute starting character');else{let i=r.dependencies,o={index:s,binding:n,part:r,evaluator:t};for(let n=0;n<i.length;n++){let r=i[n];"string"==typeof r&&(r=Fe(r),r.wildcard=!0),t._addTemplatePropertyEffect(e,r.rootProperty,{fn:Ne,info:o,trigger:r})}}}function Ne(t,e,n,r,s,i,o){let a=o[s.index],l=s.binding,h=s.part;if(i&&h.source&&e.length>h.source.length&&"property"==l.kind&&!l.isCompound&&a.__isPropertyEffectsClient&&a.__dataHasAccessor&&a.__dataHasAccessor[l.target]){let r=n[e];e=Dt(h.source,l.target,e),a._setPendingPropertyOrPath(e,r,!1,!0)&&t._enqueueClient(a)}else{let o=s.evaluator._evaluateBinding(t,h,e,n,r,i);o!==ce&&function(t,e,n,r,s){s=function(t,e,n,r){if(n.isCompound){let s=t.__dataCompoundStorage[n.target];s[r.compoundIndex]=e,e=s.join("")}"attribute"!==n.kind&&("textContent"!==n.target&&("value"!==n.target||"input"!==t.localName&&"textarea"!==t.localName)||(e=null==e?"":e));return e}(e,s,n,r),T&&(s=T(s,n.target,n.kind,e));if("attribute"==n.kind)t._valueToNodeAttribute(e,s,n.target);else{let r=n.target;e.__isPropertyEffectsClient&&e.__dataHasAccessor&&e.__dataHasAccessor[r]?e[de.READ_ONLY]&&e[de.READ_ONLY][r]||e._setPendingProperty(r,s)&&t._enqueueClient(e):t._setUnmanagedPropertyToNode(e,r,s)}}(t,a,l,h,o)}}function xe(t,e){if(e.isCompound){let n=t.__dataCompoundStorage||(t.__dataCompoundStorage={}),r=e.parts,s=new Array(r.length);for(let t=0;t<r.length;t++)s[t]=r[t].literal;let i=e.target;n[i]=s,e.literal&&"property"==e.kind&&("className"===i&&(t=I(t)),t[i]=e.literal)}}function Se(t,e,n){if(n.listenerEvent){let r=n.parts[0];t.addEventListener(n.listenerEvent,(function(t){!function(t,e,n,r,s){let i,o=t.detail,a=o&&o.path;a?(r=Dt(n,r,a),i=o&&o.value):i=t.currentTarget[n],i=s?!i:i,e[de.READ_ONLY]&&e[de.READ_ONLY][r]||!e._setPendingPropertyOrPath(r,i,!0,Boolean(a))||o&&o.queueProperty||e._invalidateProperties()}(t,e,n.target,r.source,r.negate)}))}}function Ie(t,e,n,r,s,i){i=e.static||i&&("object"!=typeof i||i[e.methodName]);let o={methodName:e.methodName,args:e.args,methodInfo:s,dynamicFn:i};for(let s,i=0;i<e.args.length&&(s=e.args[i]);i++)s.literal||t._addPropertyEffect(s.rootProperty,n,{fn:r,info:o,trigger:s});return i&&t._addPropertyEffect(e.methodName,n,{fn:r,info:o}),o}function ke(t,e,n,r,s){let i=t._methodHost||t,o=i[s.methodName];if(o){let r=t._marshalArgs(s.args,e,n);return r===ce?ce:o.apply(i,r)}s.dynamicFn||console.warn("method `"+s.methodName+"` not defined")}const Le=[],Me=new RegExp("(\\[\\[|{{)\\s*(?:(!)\\s*)?((?:[a-zA-Z_$][\\w.:$\\-*]*)\\s*(?:\\(\\s*(?:(?:(?:((?:[a-zA-Z_$][\\w.:$\\-*]*)|(?:[-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?)|(?:(?:'(?:[^'\\\\]|\\\\.)*')|(?:\"(?:[^\"\\\\]|\\\\.)*\")))\\s*)(?:,\\s*(?:((?:[a-zA-Z_$][\\w.:$\\-*]*)|(?:[-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?)|(?:(?:'(?:[^'\\\\]|\\\\.)*')|(?:\"(?:[^\"\\\\]|\\\\.)*\")))\\s*))*)?)\\)\\s*)?)(?:]]|}})","g");function De(t){let e="";for(let n=0;n<t.length;n++){e+=t[n].literal||""}return e}function Re(t){let e=t.match(/([^\s]+?)\(([\s\S]*)\)/);if(e){let t={methodName:e[1],static:!0,args:Le};if(e[2].trim()){return function(t,e){return e.args=t.map((function(t){let n=Fe(t);return n.literal||(e.static=!1),n}),this),e}(e[2].replace(/\\,/g,",").split(","),t)}return t}return null}function Fe(t){let e=t.trim().replace(/,/g,",").replace(/\\(.)/g,"$1"),n={name:e,value:"",literal:!1},r=e[0];switch("-"===r&&(r=e[1]),r>="0"&&r<="9"&&(r="#"),r){case"'":case'"':n.value=e.slice(1,-1),n.literal=!0;break;case"#":n.value=Number(e),n.literal=!0}return n.literal||(n.rootProperty=kt(e),n.structured=It(e),n.structured&&(n.wildcard=".*"==e.slice(-2),n.wildcard&&(n.name=e.slice(0,-2)))),n}function He(t,e,n){let r=zt(t,n);return void 0===r&&(r=e[n]),r}function ze(t,e,n,r){const s={indexSplices:r};t.notifyPath(n+".splices",s),t.notifyPath(n+".length",e.length)}function je(t,e,n,r,s,i){ze(t,e,n,[{index:r,addedCount:s,removed:i,object:e,type:"splice"}])}const Be=d((t=>{const e=le(Zt(t));return class extends e{constructor(){super(),this.__isPropertyEffectsClient=!0,this.__dataClientsReady,this.__dataPendingClients,this.__dataToNotify,this.__dataLinkedPaths,this.__dataHasPaths,this.__dataCompoundStorage,this.__dataHost,this.__dataTemp,this.__dataClientsInitialized,this.__data,this.__dataPending,this.__dataOld,this.__computeEffects,this.__computeInfo,this.__reflectEffects,this.__notifyEffects,this.__propagateEffects,this.__observeEffects,this.__readOnly,this.__templateInfo,this._overrideLegacyUndefined}get PROPERTY_EFFECT_TYPES(){return de}_initializeProperties(){super._initializeProperties(),this._registerHost(),this.__dataClientsReady=!1,this.__dataPendingClients=null,this.__dataToNotify=null,this.__dataLinkedPaths=null,this.__dataHasPaths=!1,this.__dataCompoundStorage=this.__dataCompoundStorage||null,this.__dataHost=this.__dataHost||null,this.__dataTemp={},this.__dataClientsInitialized=!1}_registerHost(){if(Je.length){let t=Je[Je.length-1];t._enqueueClient(this),this.__dataHost=t}}_initializeProtoProperties(t){this.__data=Object.create(t),this.__dataPending=Object.create(t),this.__dataOld={}}_initializeInstanceProperties(t){let e=this[de.READ_ONLY];for(let n in t)e&&e[n]||(this.__dataPending=this.__dataPending||{},this.__dataOld=this.__dataOld||{},this.__data[n]=this.__dataPending[n]=t[n])}_addPropertyEffect(t,e,n){this._createPropertyAccessor(t,e==de.READ_ONLY);let r=ue(this,e,!0)[t];r||(r=this[e][t]=[]),r.push(n)}_removePropertyEffect(t,e,n){let r=ue(this,e,!0)[t],s=r.indexOf(n);s>=0&&r.splice(s,1)}_hasPropertyEffect(t,e){let n=this[e];return Boolean(n&&n[t])}_hasReadOnlyEffect(t){return this._hasPropertyEffect(t,de.READ_ONLY)}_hasNotifyEffect(t){return this._hasPropertyEffect(t,de.NOTIFY)}_hasReflectEffect(t){return this._hasPropertyEffect(t,de.REFLECT)}_hasComputedEffect(t){return this._hasPropertyEffect(t,de.COMPUTE)}_setPendingPropertyOrPath(t,e,n,r){if(r||kt(Array.isArray(t)?t[0]:t)!==t){if(!r){let n=zt(this,t);if(!(t=jt(this,t,e))||!super._shouldPropertyChange(t,e,n))return!1}if(this.__dataHasPaths=!0,this._setPendingProperty(t,e,n))return function(t,e,n){let r=t.__dataLinkedPaths;if(r){let s;for(let i in r){let o=r[i];Mt(i,e)?(s=Dt(i,o,e),t._setPendingPropertyOrPath(s,n,!0,!0)):Mt(o,e)&&(s=Dt(o,i,e),t._setPendingPropertyOrPath(s,n,!0,!0))}}}(this,t,e),!0}else{if(this.__dataHasAccessor&&this.__dataHasAccessor[t])return this._setPendingProperty(t,e,n);this[t]=e}return!1}_setUnmanagedPropertyToNode(t,e,n){n===t[e]&&"object"!=typeof n||("className"===e&&(t=I(t)),t[e]=n)}_setPendingProperty(t,e,n){let r=this.__dataHasPaths&&It(t),s=r?this.__dataTemp:this.__data;return!!this._shouldPropertyChange(t,e,s[t])&&(this.__dataPending||(this.__dataPending={},this.__dataOld={}),t in this.__dataOld||(this.__dataOld[t]=this.__data[t]),r?this.__dataTemp[t]=e:this.__data[t]=e,this.__dataPending[t]=e,(r||this[de.NOTIFY]&&this[de.NOTIFY][t])&&(this.__dataToNotify=this.__dataToNotify||{},this.__dataToNotify[t]=n),!0)}_setProperty(t,e){this._setPendingProperty(t,e,!0)&&this._invalidateProperties()}_invalidateProperties(){this.__dataReady&&this._flushProperties()}_enqueueClient(t){this.__dataPendingClients=this.__dataPendingClients||[],t!==this&&this.__dataPendingClients.push(t)}_flushClients(){this.__dataClientsReady?this.__enableOrFlushClients():(this.__dataClientsReady=!0,this._readyClients(),this.__dataReady=!0)}__enableOrFlushClients(){let t=this.__dataPendingClients;if(t){this.__dataPendingClients=null;for(let e=0;e<t.length;e++){let n=t[e];n.__dataEnabled?n.__dataPending&&n._flushProperties():n._enableProperties()}}}_readyClients(){this.__enableOrFlushClients()}setProperties(t,e){for(let n in t)!e&&this[de.READ_ONLY]&&this[de.READ_ONLY][n]||this._setPendingPropertyOrPath(n,t[n],!0);this._invalidateProperties()}ready(){this._flushProperties(),this.__dataClientsReady||this._flushClients(),this.__dataPending&&this._flushProperties()}_propertiesChanged(t,e,n){let r,s=this.__dataHasPaths;this.__dataHasPaths=!1,Ce(this,e,n,s),r=this.__dataToNotify,this.__dataToNotify=null,this._propagatePropertyChanges(e,n,s),this._flushClients(),pe(this,this[de.REFLECT],e,n,s),pe(this,this[de.OBSERVE],e,n,s),r&&function(t,e,n,r,s){let i,o,a=t[de.NOTIFY],l=he++;for(let o in e)e[o]&&(a&&fe(t,a,l,o,n,r,s)||s&&ge(t,o,n))&&(i=!0);i&&(o=t.__dataHost)&&o._invalidateProperties&&o._invalidateProperties()}(this,r,e,n,s),1==this.__dataCounter&&(this.__dataTemp={})}_propagatePropertyChanges(t,e,n){this[de.PROPAGATE]&&pe(this,this[de.PROPAGATE],t,e,n),this.__templateInfo&&this._runEffectsForTemplate(this.__templateInfo,t,e,n)}_runEffectsForTemplate(t,e,n,r){const s=(e,r)=>{pe(this,t.propertyEffects,e,n,r,t.nodeList);for(let s=t.firstChild;s;s=s.nextSibling)this._runEffectsForTemplate(s,e,n,r)};t.runEffects?t.runEffects(s,e,r):s(e,r)}linkPaths(t,e){t=Ft(t),e=Ft(e),this.__dataLinkedPaths=this.__dataLinkedPaths||{},this.__dataLinkedPaths[t]=e}unlinkPaths(t){t=Ft(t),this.__dataLinkedPaths&&delete this.__dataLinkedPaths[t]}notifySplices(t,e){let n={path:""};ze(this,zt(this,t,n),n.path,e)}get(t,e){return zt(e||this,t)}set(t,e,n){n?jt(n,t,e):this[de.READ_ONLY]&&this[de.READ_ONLY][t]||this._setPendingPropertyOrPath(t,e,!0)&&this._invalidateProperties()}push(t,...e){let n={path:""},r=zt(this,t,n),s=r.length,i=r.push(...e);return e.length&&je(this,r,n.path,s,e.length,[]),i}pop(t){let e={path:""},n=zt(this,t,e),r=Boolean(n.length),s=n.pop();return r&&je(this,n,e.path,n.length,0,[s]),s}splice(t,e,n,...r){let s,i={path:""},o=zt(this,t,i);return e<0?e=o.length-Math.floor(-e):e&&(e=Math.floor(e)),s=2===arguments.length?o.splice(e):o.splice(e,n,...r),(r.length||s.length)&&je(this,o,i.path,e,r.length,s),s}shift(t){let e={path:""},n=zt(this,t,e),r=Boolean(n.length),s=n.shift();return r&&je(this,n,e.path,0,0,[s]),s}unshift(t,...e){let n={path:""},r=zt(this,t,n),s=r.unshift(...e);return e.length&&je(this,r,n.path,0,e.length,[]),s}notifyPath(t,e){let n;if(1==arguments.length){let r={path:""};e=zt(this,t,r),n=r.path}else n=Array.isArray(t)?Ft(t):t;this._setPendingPropertyOrPath(n,e,!0,!0)&&this._invalidateProperties()}_createReadOnlyProperty(t,e){var n;this._addPropertyEffect(t,de.READ_ONLY),e&&(this["_set"+(n=t,n[0].toUpperCase()+n.substring(1))]=function(e){this._setProperty(t,e)})}_createPropertyObserver(t,e,n){let r={property:t,method:e,dynamicFn:Boolean(n)};this._addPropertyEffect(t,de.OBSERVE,{fn:ye,info:r,trigger:{name:t}}),n&&this._addPropertyEffect(e,de.OBSERVE,{fn:ye,info:r,trigger:{name:e}})}_createMethodObserver(t,e){let n=Re(t);if(!n)throw new Error("Malformed observer expression '"+t+"'");Ie(this,n,de.OBSERVE,ke,null,e)}_createNotifyingProperty(t){this._addPropertyEffect(t,de.NOTIFY,{fn:Pe,info:{eventName:Ut(t)+"-changed",property:t}})}_createReflectedProperty(t){let e=this.constructor.attributeNameForProperty(t);"-"===e[0]?console.warn("Property "+t+" cannot be reflected to attribute "+e+' because "-" is not a valid starting attribute name. Use a lowercase first letter for the property instead.'):this._addPropertyEffect(t,de.REFLECT,{fn:ve,info:{attrName:e}})}_createComputedProperty(t,e,n){let r=Re(e);if(!r)throw new Error("Malformed computed expression '"+e+"'");const s=Ie(this,r,de.COMPUTE,Te,t,n);ue(this,"__computeInfo")[t]=s}_marshalArgs(t,e,n){const r=this.__data,s=[];for(let i=0,o=t.length;i<o;i++){let{name:o,structured:a,wildcard:l,value:h,literal:c}=t[i];if(!c)if(l){const t=Mt(o,e),s=He(r,n,t?e:o);h={path:t?e:o,value:s,base:t?zt(r,o):s}}else h=a?He(r,n,o):r[o];s[i]=h}return s}static addPropertyEffect(t,e,n){this.prototype._addPropertyEffect(t,e,n)}static createPropertyObserver(t,e,n){this.prototype._createPropertyObserver(t,e,n)}static createMethodObserver(t,e){this.prototype._createMethodObserver(t,e)}static createNotifyingProperty(t){this.prototype._createNotifyingProperty(t)}static createReadOnlyProperty(t,e){this.prototype._createReadOnlyProperty(t,e)}static createReflectedProperty(t){this.prototype._createReflectedProperty(t)}static createComputedProperty(t,e,n){this.prototype._createComputedProperty(t,e,n)}static bindTemplate(t){return this.prototype._bindTemplate(t)}_bindTemplate(t,e){let n=this.constructor._parseTemplate(t),r=this.__preBoundTemplateInfo==n;if(!r)for(let t in n.propertyEffects)this._createPropertyAccessor(t);if(e)if(n=Object.create(n),n.wasPreBound=r,this.__templateInfo){const e=t._parentTemplateInfo||this.__templateInfo,r=e.lastChild;n.parent=e,e.lastChild=n,n.previousSibling=r,r?r.nextSibling=n:e.firstChild=n}else this.__templateInfo=n;else this.__preBoundTemplateInfo=n;return n}static _addTemplatePropertyEffect(t,e,n){(t.hostProps=t.hostProps||{})[e]=!0;let r=t.propertyEffects=t.propertyEffects||{};(r[e]=r[e]||[]).push(n)}_stampTemplate(t,e){e=e||this._bindTemplate(t,!0),Je.push(this);let n=super._stampTemplate(t,e);if(Je.pop(),e.nodeList=n.nodeList,!e.wasPreBound){let t=e.childNodes=[];for(let e=n.firstChild;e;e=e.nextSibling)t.push(e)}return n.templateInfo=e,function(t,e){let{nodeList:n,nodeInfoList:r}=e;if(r.length)for(let e=0;e<r.length;e++){let s=r[e],i=n[e],o=s.bindings;if(o)for(let e=0;e<o.length;e++){let n=o[e];xe(i,n),Se(i,t,n)}i.__dataHost=t}}(this,e),this.__dataClientsReady&&(this._runEffectsForTemplate(e,this.__data,null,!1),this._flushClients()),n}_removeBoundDom(t){const e=t.templateInfo,{previousSibling:n,nextSibling:r,parent:s}=e;n?n.nextSibling=r:s&&(s.firstChild=r),r?r.previousSibling=n:s&&(s.lastChild=n),e.nextSibling=e.previousSibling=null;let i=e.childNodes;for(let t=0;t<i.length;t++){let e=i[t];I(I(e).parentNode).removeChild(e)}}static _parseTemplateNode(t,n,r){let s=e._parseTemplateNode.call(this,t,n,r);if(t.nodeType===Node.TEXT_NODE){let e=this._parseBindings(t.textContent,n);e&&(t.textContent=De(e)||" ",Oe(this,n,r,"text","textContent",e),s=!0)}return s}static _parseTemplateNodeAttribute(t,n,r,s,i){let o=this._parseBindings(i,n);if(o){let e=s,i="property";_e.test(s)?i="attribute":"$"==s[s.length-1]&&(s=s.slice(0,-1),i="attribute");let a=De(o);return a&&"attribute"==i&&("class"==s&&t.hasAttribute("class")&&(a+=" "+t.getAttribute(s)),t.setAttribute(s,a)),"attribute"==i&&"disable-upgrade$"==e&&t.setAttribute(s,""),"input"===t.localName&&"value"===e&&t.setAttribute(e,""),t.removeAttribute(e),"property"===i&&(s=Yt(s)),Oe(this,n,r,i,s,o,a),!0}return e._parseTemplateNodeAttribute.call(this,t,n,r,s,i)}static _parseTemplateNestedTemplate(t,n,r){let s=e._parseTemplateNestedTemplate.call(this,t,n,r);const i=t.parentNode,o=r.templateInfo;i.localName,i.localName;let a=o.hostProps;{let t="{";for(let e in a){Oe(this,n,r,"property","_host_"+e,[{mode:t,source:e,dependencies:[e],hostProp:!0}])}}return s}static _parseBindings(t,e){let n,r=[],s=0;for(;null!==(n=Me.exec(t));){n.index>s&&r.push({literal:t.slice(s,n.index)});let i=n[1][0],o=Boolean(n[2]),a=n[3].trim(),l=!1,h="",c=-1;"{"==i&&(c=a.indexOf("::"))>0&&(h=a.substring(c+2),a=a.substring(0,c),l=!0);let d=Re(a),_=[];if(d){let{args:t,methodName:n}=d;for(let e=0;e<t.length;e++){let n=t[e];n.literal||_.push(n)}let r=e.dynamicFns;(r&&r[n]||d.static)&&(_.push(n),d.dynamicFn=!0)}else _.push(a);r.push({source:a,mode:i,negate:o,customEvent:l,signature:d,dependencies:_,event:h}),s=Me.lastIndex}if(s&&s<t.length){let e=t.substring(s);e&&r.push({literal:e})}return r.length?r:null}static _evaluateBinding(t,e,n,r,s,i){let o;return o=e.signature?ke(t,n,r,0,e.signature):n!=e.source?zt(t,e.source):i&&It(n)?zt(t,n):t.__data[n],e.negate&&(o=!o),o}}})),Je=[];const qe=d((t=>{const e=Vt(t);function n(t){const e=Object.getPrototypeOf(t);return e.prototype instanceof s?e:null}function r(t){if(!t.hasOwnProperty(JSCompiler_renameProperty("__ownProperties",t))){let e=null;if(t.hasOwnProperty(JSCompiler_renameProperty("properties",t))){const n=t.properties;n&&(e=function(t){const e={};for(let n in t){const r=t[n];e[n]="function"==typeof r?{type:r}:r}return e}(n))}t.__ownProperties=e}return t.__ownProperties}class s extends e{static get observedAttributes(){if(!this.hasOwnProperty(JSCompiler_renameProperty("__observedAttributes",this))){this.prototype;const t=this._properties;this.__observedAttributes=t?Object.keys(t).map((t=>this.prototype._addPropertyToAttributeMap(t))):[]}return this.__observedAttributes}static finalize(){if(!this.hasOwnProperty(JSCompiler_renameProperty("__finalized",this))){const t=n(this);t&&t.finalize(),this.__finalized=!0,this._finalizeClass()}}static _finalizeClass(){const t=r(this);t&&this.createProperties(t)}static get _properties(){if(!this.hasOwnProperty(JSCompiler_renameProperty("__properties",this))){const t=n(this);this.__properties=Object.assign({},t&&t._properties,r(this))}return this.__properties}static typeForProperty(t){const e=this._properties[t];return e&&e.type}_initializeProperties(){this.constructor.finalize(),super._initializeProperties()}connectedCallback(){super.connectedCallback&&super.connectedCallback(),this._enableProperties()}disconnectedCallback(){super.disconnectedCallback&&super.disconnectedCallback()}}return s})),Ye=d((t=>{const e=qe(Be(t));function n(t,e,n,r){n.computed&&(n.readOnly=!0),n.computed&&(t._hasReadOnlyEffect(e)?console.warn(`Cannot redefine computed property '${e}'.`):t._createComputedProperty(e,n.computed,r)),n.readOnly&&!t._hasReadOnlyEffect(e)?t._createReadOnlyProperty(e,!n.computed):!1===n.readOnly&&t._hasReadOnlyEffect(e)&&console.warn(`Cannot make readOnly property '${e}' non-readOnly.`),n.reflectToAttribute&&!t._hasReflectEffect(e)?t._createReflectedProperty(e):!1===n.reflectToAttribute&&t._hasReflectEffect(e)&&console.warn(`Cannot make reflected property '${e}' non-reflected.`),n.notify&&!t._hasNotifyEffect(e)?t._createNotifyingProperty(e):!1===n.notify&&t._hasNotifyEffect(e)&&console.warn(`Cannot make notify property '${e}' non-notify.`),n.observer&&t._createPropertyObserver(e,n.observer,r[n.observer]),t._addPropertyToAttributeMap(e)}function r(t,e,n,r){{const s=e.content.querySelectorAll("style"),i=Nt(e),o=function(t){let e=Et(t);return e?xt(e):[]}(n),a=e.content.firstElementChild;for(let n=0;n<o.length;n++){let s=o[n];s.textContent=t._processStyleText(s.textContent,r),e.content.insertBefore(s,a)}let l=0;for(let e=0;e<i.length;e++){let n=i[e],o=s[l];o!==n?(n=n.cloneNode(!0),o.parentNode.insertBefore(n,o)):l++,n.textContent=t._processStyleText(n.textContent,r)}}}return class extends e{static get polymerElementVersion(){return"3.5.0"}static _finalizeClass(){e._finalizeClass.call(this);const t=((n=this).hasOwnProperty(JSCompiler_renameProperty("__ownObservers",n))||(n.__ownObservers=n.hasOwnProperty(JSCompiler_renameProperty("observers",n))?n.observers:null),n.__ownObservers);var n;t&&this.createObservers(t,this._properties),this._prepareTemplate()}static _prepareTemplate(){let t=this.template;t&&("string"==typeof t?(console.error("template getter must return HTMLTemplateElement"),t=null):x||(t=t.cloneNode(!0))),this.prototype._template=t}static createProperties(t){for(let e in t)n(this.prototype,e,t[e],t)}static createObservers(t,e){const n=this.prototype;for(let r=0;r<t.length;r++)n._createMethodObserver(t[r],e)}static get template(){if(!this.hasOwnProperty(JSCompiler_renameProperty("_template",this))){let t=this.prototype.hasOwnProperty(JSCompiler_renameProperty("_template",this.prototype))?this.prototype._template:void 0;"function"==typeof t&&(t=t()),this._template=void 0!==t?t:this.hasOwnProperty(JSCompiler_renameProperty("is",this))&&function(t){let e=null;if(t&&(!A||N)&&(e=wt.import(t,"template"),A&&!e))throw new Error("strictTemplatePolicy: expecting dom-module or null template for "+t);return e}(this.is)||Object.getPrototypeOf(this.prototype).constructor.template}return this._template}static set template(t){this._template=t}static get importPath(){if(!this.hasOwnProperty(JSCompiler_renameProperty("_importPath",this))){const t=this.importMeta;if(t)this._importPath=C(t.url);else{const t=wt.import(this.is);this._importPath=t&&t.assetpath||Object.getPrototypeOf(this.prototype).constructor.importPath}}return this._importPath}constructor(){super(),this._template,this._importPath,this.rootPath,this.importPath,this.root,this.$}_initializeProperties(){this.constructor.finalize(),this.constructor._finalizeTemplate(this.localName),super._initializeProperties(),this.rootPath=E,this.importPath=this.constructor.importPath;let t=function(t){if(!t.hasOwnProperty(JSCompiler_renameProperty("__propertyDefaults",t))){t.__propertyDefaults=null;let e=t._properties;for(let n in e){let r=e[n];"value"in r&&(t.__propertyDefaults=t.__propertyDefaults||{},t.__propertyDefaults[n]=r)}}return t.__propertyDefaults}(this.constructor);if(t)for(let e in t){let n=t[e];if(this._canApplyPropertyDefault(e)){let t="function"==typeof n.value?n.value.call(this):n.value;this._hasAccessor(e)?this._setPendingProperty(e,t,!0):this[e]=t}}}_canApplyPropertyDefault(t){return!this.hasOwnProperty(t)}static _processStyleText(t,e){return v(t,e)}static _finalizeTemplate(t){const e=this.prototype._template;if(e&&!e.__polymerFinalized){e.__polymerFinalized=!0;const n=this.importPath;r(this,e,t,n?P(n):""),this.prototype._bindTemplate(e)}}connectedCallback(){super.connectedCallback()}ready(){this._template&&(this.root=this._stampTemplate(this._template),this.$=this.root.$),super.ready()}_readyClients(){this._template&&(this.root=this._attachDom(this.root)),super._readyClients()}_attachDom(t){const e=I(this);if(e.attachShadow)return t?(e.shadowRoot||(e.attachShadow({mode:"open",shadyUpgradeFragment:t}),this.constructor._styleSheet&&(e.shadowRoot.adoptedStyleSheets=[this.constructor._styleSheet])),e.shadowRoot.appendChild(t),e.shadowRoot):null;throw new Error("ShadowDOM not available. PolymerElement can create dom as children instead of in ShadowDOM by setting `this.root = this;` before `ready`.")}updateStyles(t){for(const[e,n]of Object.entries(t))this.style.setProperty(e,n)}resolveUrl(t,e){return!e&&this.importPath&&(e=P(this.importPath)),P(t,e)}static _parseTemplateContent(t,n,r){return n.dynamicFns=n.dynamicFns||this._properties,e._parseTemplateContent.call(this,t,n,r)}static _addTemplatePropertyEffect(t,n,r){return e._addTemplatePropertyEffect.call(this,t,n,r)}}})),Ue=window.trustedTypes&&trustedTypes.createPolicy("polymer-html-literal",{createHTML:t=>t});class $e{constructor(t,e){Ge(t,e);const n=e.reduce(((e,n,r)=>e+Ve(n)+t[r+1]),t[0]);this.value=n.toString()}toString(){return this.value}}function Ve(t){if(t instanceof $e)return t.value;throw new Error("non-literal value passed to Polymer's htmlLiteral function: "+t)}const Xe=function(t,...e){Ge(t,e);const n=document.createElement("template");let r=e.reduce(((e,n,r)=>e+function(t){if(t instanceof HTMLTemplateElement)return t.innerHTML;if(t instanceof $e)return Ve(t);throw new Error("non-template value passed to Polymer's html function: "+t)}(n)+t[r+1]),t[0]);return Ue&&(r=Ue.createHTML(r)),n.innerHTML=r,n},Ge=(t,e)=>{if(!Array.isArray(t)||!Array.isArray(t.raw)||e.length!==t.length-1)throw new TypeError("Invalid call to the html template tag")},We=Ye(HTMLElement),Ze=function(){let t,e;do{t=!1,e=f()}while(e)};function Ke(t,e,n,r,s){let i;s&&(i="object"==typeof n&&null!==n,i&&(r=t.__dataTemp[e]));let o=r!==n&&(r==r||n==n);return i&&o&&(t.__dataTemp[e]=n),o}const Qe=d((t=>class extends t{_shouldPropertyChange(t,e,n){return Ke(this,t,e,n,!0)}})),tn=d((t=>class extends t{static get properties(){return{mutableData:Boolean}}_shouldPropertyChange(t,e,n){return Ke(this,t,e,n,this.mutableData)}}));Qe._mutablePropertyChange=Ke;let en=null;function nn(){return en}nn.prototype=Object.create(HTMLTemplateElement.prototype,{constructor:{value:nn,writable:!0}});const rn=Be(nn),sn=Qe(rn);const on=Be(class{});class an extends on{constructor(t){super(),this._configureProperties(t),this.root=this._stampTemplate(this.__dataHost);let e=[];this.children=e;for(let t=this.root.firstChild;t;t=t.nextSibling)e.push(t),t.__templatizeInstance=this;this.__templatizeOwner&&this.__templatizeOwner.__hideTemplateChildren__&&this._showHideChildren(!0);let n=this.__templatizeOptions;(t&&n.instanceProps||!n.instanceProps)&&this._enableProperties()}_configureProperties(t){if(this.__templatizeOptions.forwardHostProp)for(let t in this.__hostProps)this._setPendingProperty(t,this.__dataHost["_host_"+t]);for(let e in t)this._setPendingProperty(e,t[e])}forwardHostProp(t,e){this._setPendingPropertyOrPath(t,e,!1,!0)&&this.__dataHost._enqueueClient(this)}_addEventListenerToNode(t,e,n){if(this._methodHost&&this.__templatizeOptions.parentModel)this._methodHost._addEventListenerToNode(t,e,(t=>{t.model=this,n(t)}));else{let r=this.__dataHost.__dataHost;r&&r._addEventListenerToNode(t,e,n)}}_showHideChildren(t){!function(t,e){for(let n=0;n<e.length;n++){let r=e[n];if(Boolean(t)!=Boolean(r.__hideTemplateChildren__))if(r.nodeType===Node.TEXT_NODE)t?(r.__polymerTextContent__=r.textContent,r.textContent=""):r.textContent=r.__polymerTextContent__;else if("slot"===r.localName)if(t)r.__polymerReplaced__=document.createComment("hidden-slot"),I(I(r).parentNode).replaceChild(r.__polymerReplaced__,r);else{const t=r.__polymerReplaced__;t&&I(I(t).parentNode).replaceChild(r,t)}else r.style&&(t?(r.__polymerDisplay__=r.style.display,r.style.display="none"):r.style.display=r.__polymerDisplay__);r.__hideTemplateChildren__=t,r._showHideChildren&&r._showHideChildren(t)}}(t,this.children)}_setUnmanagedPropertyToNode(t,e,n){t.__hideTemplateChildren__&&t.nodeType==Node.TEXT_NODE&&"textContent"==e?t.__polymerTextContent__=n:super._setUnmanagedPropertyToNode(t,e,n)}get parentModel(){let t=this.__parentModel;if(!t){let e;t=this;do{t=t.__dataHost.__dataHost}while((e=t.__templatizeOptions)&&!e.parentModel);this.__parentModel=t}return t}dispatchEvent(t){return!0}}an.prototype.__dataHost,an.prototype.__templatizeOptions,an.prototype._methodHost,an.prototype.__templatizeOwner,an.prototype.__hostProps;const ln=Qe(an);function hn(t){let e=t.__dataHost;return e&&e._methodHost||e}function cn(t,e,n){let r=n.mutableData?ln:an;pn.mixin&&(r=pn.mixin(r));let s=class extends r{};return s.prototype.__templatizeOptions=n,s.prototype._bindTemplate(t),function(t,e,n,r){let s=n.hostProps||{};for(let e in r.instanceProps){delete s[e];let n=r.notifyInstanceProp;n&&t.prototype._addPropertyEffect(e,t.prototype.PROPERTY_EFFECT_TYPES.NOTIFY,{fn:un(e,n)})}if(r.forwardHostProp&&e.__dataHost)for(let e in s)n.hasHostProps||(n.hasHostProps=!0),t.prototype._addPropertyEffect(e,t.prototype.PROPERTY_EFFECT_TYPES.NOTIFY,{fn:function(t,e,n){t.__dataHost._setPendingPropertyOrPath("_host_"+e,n[e],!0,!0)}})}(s,t,e,n),s}function dn(t,e,n,r){let s=n.forwardHostProp;if(s&&e.hasHostProps){const r="template"==t.localName;let a=e.templatizeTemplateClass;if(!a){if(r){let t=n.mutableData?sn:rn;class r extends t{}a=e.templatizeTemplateClass=r}else{const n=t.constructor;class r extends n{}a=e.templatizeTemplateClass=r}let i=e.hostProps;for(let t in i)a.prototype._addPropertyEffect("_host_"+t,a.prototype.PROPERTY_EFFECT_TYPES.PROPAGATE,{fn:_n(t,s)}),a.prototype._createNotifyingProperty("_host_"+t)}if(t.__dataProto&&Object.assign(t.__data,t.__dataProto),r)o=a,en=i=t,Object.setPrototypeOf(i,o.prototype),new o,en=null,t.__dataTemp={},t.__dataPending=null,t.__dataOld=null,t._enableProperties();else{Object.setPrototypeOf(t,a.prototype);const n=e.hostProps;for(let e in n)if(e="_host_"+e,e in t){const n=t[e];delete t[e],t.__data[e]=n}}}var i,o}function _n(t,e){return function(t,n,r){e.call(t.__templatizeOwner,n.substring("_host_".length),r[n])}}function un(t,e){return function(t,n,r){e.call(t.__templatizeOwner,t,n,r[n])}}function pn(t,e,n){if(A&&!hn(t))throw new Error("strictTemplatePolicy: template owner not trusted");if(n=n||{},t.__templatizeOwner)throw new Error("A <template> can only be templatized once");t.__templatizeOwner=e;let r=(e?e.constructor:an)._parseTemplate(t),s=r.templatizeInstanceClass;s||(s=cn(t,r,n),r.templatizeInstanceClass=s);const i=hn(t);dn(t,r,n);let o=class extends s{};return o.prototype._methodHost=i,o.prototype.__dataHost=t,o.prototype.__templatizeOwner=e,o.prototype.__hostProps=r.hostProps,o=o,o}function fn(t,e){let n;for(;e;)if(n=e.__dataHost?e:e.__templatizeInstance){if(n.__dataHost==t)return n;e=n.__dataHost}else e=I(e).parentNode;return null}class mn extends We{static get is(){return"dom-if"}static get template(){return null}static get properties(){return{if:{type:Boolean,observer:"__debounceRender"},restamp:{type:Boolean,observer:"__debounceRender"},notifyDomChange:{type:Boolean}}}constructor(){super(),this.__renderDebouncer=null,this._lastIf=!1,this.__hideTemplateChildren__=!1,this.__template,this._templateInfo}__debounceRender(){this.__renderDebouncer=_.debounce(this.__renderDebouncer,h,(()=>this.__render())),p(this.__renderDebouncer)}disconnectedCallback(){super.disconnectedCallback();const t=I(this).parentNode;t&&(t.nodeType!=Node.DOCUMENT_FRAGMENT_NODE||I(t).host)||this.__teardownInstance()}connectedCallback(){super.connectedCallback(),this.style.display="none",this.if&&this.__debounceRender()}__ensureTemplate(){if(!this.__template){const t=this;let e=t._templateInfo?t:I(t).querySelector("template");if(!e){let t=new MutationObserver((()=>{if(!I(this).querySelector("template"))throw new Error("dom-if requires a <template> child");t.disconnect(),this.__render()}));return t.observe(this,{childList:!0}),!1}this.__template=e}return!0}__ensureInstance(){let t=I(this).parentNode;if(this.__hasInstance()){let e=this.__getInstanceNodes();if(e&&e.length){if(I(this).previousSibling!==e[e.length-1])for(let n,r=0;r<e.length&&(n=e[r]);r++)I(t).insertBefore(n,this)}}else{if(!t)return!1;if(!this.__ensureTemplate())return!1;this.__createAndInsertInstance(t)}return!0}render(){Ze()}__render(){if(this.if){if(!this.__ensureInstance())return}else this.restamp&&this.__teardownInstance();this._showHideChildren(),this.if!=this._lastIf&&(this.dispatchEvent(new CustomEvent("dom-change",{bubbles:!0,composed:!0})),this._lastIf=this.if)}__hasInstance(){}__getInstanceNodes(){}__createAndInsertInstance(t){}__teardownInstance(){}_showHideChildren(){}}const yn=class extends mn{constructor(){super(),this.__ctor=null,this.__instance=null,this.__invalidProps=null}__hasInstance(){return Boolean(this.__instance)}__getInstanceNodes(){return this.__instance.children}__createAndInsertInstance(t){this.__ctor||(this.__ctor=pn(this.__template,this,{mutableData:!0,forwardHostProp:function(t,e){this.__instance&&(this.if?this.__instance.forwardHostProp(t,e):(this.__invalidProps=this.__invalidProps||Object.create(null),this.__invalidProps[kt(t)]=!0))}})),this.__instance=new this.__ctor,I(t).insertBefore(this.__instance.root,this)}__teardownInstance(){if(this.__instance){let t=this.__instance.children;if(t&&t.length){let e=I(t[0]).parentNode;if(e){e=I(e);for(let n,r=0;r<t.length&&(n=t[r]);r++)e.removeChild(n)}}this.__invalidProps=null,this.__instance=null}}__syncHostProperties(){let t=this.__invalidProps;if(t){this.__invalidProps=null;for(let e in t)this.__instance._setPendingProperty(e,this.__dataHost[e]);this.__instance._flushProperties()}}_showHideChildren(){const t=this.__hideTemplateChildren__||!this.if;this.__instance&&Boolean(this.__instance.__hidden)!==t&&(this.__instance.__hidden=t,this.__instance._showHideChildren(t)),t||this.__syncHostProperties()}};customElements.define(yn.is,yn);const gn=tn(We);class bn extends gn{static get is(){return"dom-repeat"}static get template(){return null}static get properties(){return{items:{type:Array},as:{type:String,value:"item"},indexAs:{type:String,value:"index"},itemsIndexAs:{type:String,value:"itemsIndex"},sort:{type:Function,observer:"__sortChanged"},filter:{type:Function,observer:"__filterChanged"},observe:{type:String,observer:"__observeChanged"},delay:Number,renderedItemCount:{type:Number,notify:!0,readOnly:!0},initialCount:{type:Number},targetFramerate:{type:Number,value:20},_targetFrameTime:{type:Number,computed:"__computeFrameTime(targetFramerate)"},notifyDomChange:{type:Boolean},reuseChunkedInstances:{type:Boolean}}}static get observers(){return["__itemsChanged(items.*)"]}constructor(){super(),this.__instances=[],this.__renderDebouncer=null,this.__itemsIdxToInstIdx={},this.__chunkCount=null,this.__renderStartTime=null,this.__itemsArrayChanged=!1,this.__shouldMeasureChunk=!1,this.__shouldContinueChunking=!1,this.__chunkingId=0,this.__sortFn=null,this.__filterFn=null,this.__observePaths=null,this.__ctor=null,this.__isDetached=!0,this.template=null,this._templateInfo}disconnectedCallback(){super.disconnectedCallback(),this.__isDetached=!0;for(let t=0;t<this.__instances.length;t++)this.__detachInstance(t);this.__chunkingId&&cancelAnimationFrame(this.__chunkingId)}connectedCallback(){if(super.connectedCallback(),this.style.display="none",this.__isDetached){this.__isDetached=!1;let t=I(I(this).parentNode);for(let e=0;e<this.__instances.length;e++)this.__attachInstance(e,t);this.__chunkingId&&this.__render()}}__ensureTemplatized(){if(!this.__ctor){const t=this;let e=this.template=t._templateInfo?t:this.querySelector("template");if(!e){let t=new MutationObserver((()=>{if(!this.querySelector("template"))throw new Error("dom-repeat requires a <template> child");t.disconnect(),this.__render()}));return t.observe(this,{childList:!0}),!1}let n={};n[this.as]=!0,n[this.indexAs]=!0,n[this.itemsIndexAs]=!0,this.__ctor=pn(e,this,{mutableData:this.mutableData,parentModel:!0,instanceProps:n,forwardHostProp:function(t,e){let n=this.__instances;for(let r,s=0;s<n.length&&(r=n[s]);s++)r.forwardHostProp(t,e)},notifyInstanceProp:function(t,e,n){if(Rt(this.as,e)){let r=t[this.itemsIndexAs];e==this.as&&(this.items[r]=n);let s=Dt(this.as,`${JSCompiler_renameProperty("items",this)}.${r}`,e);this.notifyPath(s,n)}}})}return!0}__getMethodHost(){return this.__dataHost._methodHost||this.__dataHost}__functionFromPropertyValue(t){if("string"==typeof t){let e=t,n=this.__getMethodHost();return function(){return n[e].apply(n,arguments)}}return t}__sortChanged(t){this.__sortFn=this.__functionFromPropertyValue(t),this.items&&this.__debounceRender(this.__render)}__filterChanged(t){this.__filterFn=this.__functionFromPropertyValue(t),this.items&&this.__debounceRender(this.__render)}__computeFrameTime(t){return Math.ceil(1e3/t)}__observeChanged(){this.__observePaths=this.observe&&this.observe.replace(".*",".").split(" ")}__handleObservedPaths(t){if(this.__sortFn||this.__filterFn)if(t){if(this.__observePaths){let e=this.__observePaths;for(let n=0;n<e.length;n++)0===t.indexOf(e[n])&&this.__debounceRender(this.__render,this.delay)}}else this.__debounceRender(this.__render,this.delay)}__itemsChanged(t){this.items&&!Array.isArray(this.items)&&console.warn("dom-repeat expected array for `items`, found",this.items),this.__handleItemPath(t.path,t.value)||("items"===t.path&&(this.__itemsArrayChanged=!0),this.__debounceRender(this.__render))}__debounceRender(t,e=0){this.__renderDebouncer=_.debounce(this.__renderDebouncer,e>0?o.after(e):h,t.bind(this)),p(this.__renderDebouncer)}render(){this.__debounceRender(this.__render),Ze()}__render(){if(!this.__ensureTemplatized())return;let t=this.items||[];const e=this.__sortAndFilterItems(t),n=this.__calculateLimit(e.length);this.__updateInstances(t,n,e),this.initialCount&&(this.__shouldMeasureChunk||this.__shouldContinueChunking)&&(cancelAnimationFrame(this.__chunkingId),this.__chunkingId=requestAnimationFrame((()=>{this.__chunkingId=null,this.__continueChunking()}))),this._setRenderedItemCount(this.__instances.length),this.dispatchEvent(new CustomEvent("dom-change",{bubbles:!0,composed:!0}))}__sortAndFilterItems(t){let e=new Array(t.length);for(let n=0;n<t.length;n++)e[n]=n;return this.__filterFn&&(e=e.filter(((e,n,r)=>this.__filterFn(t[e],n,r)))),this.__sortFn&&e.sort(((e,n)=>this.__sortFn(t[e],t[n]))),e}__calculateLimit(t){let e=t;const n=this.__instances.length;if(this.initialCount){let r;!this.__chunkCount||this.__itemsArrayChanged&&!this.reuseChunkedInstances?(e=Math.min(t,this.initialCount),r=Math.max(e-n,0),this.__chunkCount=r||1):(r=Math.min(Math.max(t-n,0),this.__chunkCount),e=Math.min(n+r,t)),this.__shouldMeasureChunk=r===this.__chunkCount,this.__shouldContinueChunking=e<t,this.__renderStartTime=performance.now()}return this.__itemsArrayChanged=!1,e}__continueChunking(){if(this.__shouldMeasureChunk){const t=performance.now()-this.__renderStartTime,e=this._targetFrameTime/t;this.__chunkCount=Math.round(this.__chunkCount*e)||1}this.__shouldContinueChunking&&this.__debounceRender(this.__render)}__updateInstances(t,e,n){const r=this.__itemsIdxToInstIdx={};let s;for(s=0;s<e;s++){let e=this.__instances[s],i=n[s],o=t[i];r[i]=s,e?(e._setPendingProperty(this.as,o),e._setPendingProperty(this.indexAs,s),e._setPendingProperty(this.itemsIndexAs,i),e._flushProperties()):this.__insertInstance(o,s,i)}for(let t=this.__instances.length-1;t>=s;t--)this.__detachAndRemoveInstance(t)}__detachInstance(t){let e=this.__instances[t];const n=I(e.root);for(let t=0;t<e.children.length;t++){let r=e.children[t];n.appendChild(r)}return e}__attachInstance(t,e){let n=this.__instances[t];e.insertBefore(n.root,this)}__detachAndRemoveInstance(t){this.__detachInstance(t),this.__instances.splice(t,1)}__stampInstance(t,e,n){let r={};return r[this.as]=t,r[this.indexAs]=e,r[this.itemsIndexAs]=n,new this.__ctor(r)}__insertInstance(t,e,n){const r=this.__stampInstance(t,e,n);let s=this.__instances[e+1],i=s?s.children[0]:this;return I(I(this).parentNode).insertBefore(r.root,i),this.__instances[e]=r,r}_showHideChildren(t){for(let e=0;e<this.__instances.length;e++)this.__instances[e]._showHideChildren(t)}__handleItemPath(t,e){let n=t.slice(6),r=n.indexOf("."),s=r<0?n:n.substring(0,r);if(s==parseInt(s,10)){let t=r<0?"":n.substring(r+1);this.__handleObservedPaths(t);let i=this.__itemsIdxToInstIdx[s],o=this.__instances[i];if(o){let n=this.as+(t?"."+t:"");o._setPendingPropertyOrPath(n,e,!1,!0),o._flushProperties()}return!0}}itemForElement(t){let e=this.modelForElement(t);return e&&e[this.as]}indexForElement(t){let e=this.modelForElement(t);return e&&e[this.indexAs]}modelForElement(t){return fn(this.template,t)}}customElements.define(bn.is,bn);const Pn=d((t=>class extends t{_addEventListenerToNode(t,e,n){it(t,e,n)||super._addEventListenerToNode(t,e,n)}_removeEventListenerFromNode(t,e,n){ot(t,e,n)||super._removeEventListenerFromNode(t,e,n)}}));let vn=!1,Cn=[],wn=[];function En(){vn=!0,requestAnimationFrame((function(){vn=!1,function(t){for(;t.length;)Tn(t.shift())}(Cn),setTimeout((function(){!function(t){for(let e=0,n=t.length;e<n;e++)Tn(t.shift())}(wn)}))}))}function Tn(t){const e=t[0],n=t[1],r=t[2];try{n.apply(e,r)}catch(t){setTimeout((()=>{throw t}))}}function On(t,e,n){vn||En(),Cn.push([t,e,n])}function An(t,e,n){vn||En(),wn.push([t,e,n])}function Nn(){document.body.removeAttribute("unresolved")}function xn(t,e,n){return{index:t,removed:e,addedCount:n}}"interactive"===document.readyState||"complete"===document.readyState?Nn():window.addEventListener("DOMContentLoaded",Nn);function Sn(t,e,n,r,s,i){let o,a=0,l=0,h=Math.min(n-e,i-s);if(0==e&&0==s&&(a=function(t,e,n){for(let r=0;r<n;r++)if(!kn(t[r],e[r]))return r;return n}(t,r,h)),n==t.length&&i==r.length&&(l=function(t,e,n){let r=t.length,s=e.length,i=0;for(;i<n&&kn(t[--r],e[--s]);)i++;return i}(t,r,h-a)),s+=a,i-=l,(n-=l)-(e+=a)==0&&i-s==0)return[];if(e==n){for(o=xn(e,[],0);s<i;)o.removed.push(r[s++]);return[o]}if(s==i)return[xn(e,[],n-e)];let c=function(t){let e=t.length-1,n=t[0].length-1,r=t[e][n],s=[];for(;e>0||n>0;){if(0==e){s.push(2),n--;continue}if(0==n){s.push(3),e--;continue}let i,o=t[e-1][n-1],a=t[e-1][n],l=t[e][n-1];i=a<l?a<o?a:o:l<o?l:o,i==o?(o==r?s.push(0):(s.push(1),r=o),e--,n--):i==a?(s.push(3),e--,r=a):(s.push(2),n--,r=l)}return s.reverse(),s}(function(t,e,n,r,s,i){let o=i-s+1,a=n-e+1,l=new Array(o);for(let t=0;t<o;t++)l[t]=new Array(a),l[t][0]=t;for(let t=0;t<a;t++)l[0][t]=t;for(let n=1;n<o;n++)for(let i=1;i<a;i++)if(kn(t[e+i-1],r[s+n-1]))l[n][i]=l[n-1][i-1];else{let t=l[n-1][i]+1,e=l[n][i-1]+1;l[n][i]=t<e?t:e}return l}(t,e,n,r,s,i));o=void 0;let d=[],_=e,u=s;for(let t=0;t<c.length;t++)switch(c[t]){case 0:o&&(d.push(o),o=void 0),_++,u++;break;case 1:o||(o=xn(_,[],0)),o.addedCount++,_++,o.removed.push(r[u]),u++;break;case 2:o||(o=xn(_,[],0)),o.addedCount++,_++;break;case 3:o||(o=xn(_,[],0)),o.removed.push(r[u]),u++}return o&&d.push(o),d}function In(t,e){return Sn(t,0,t.length,e,0,e.length)}function kn(t,e){return t===e}function Ln(t){return"slot"===t.localName}let Mn=class{static getFlattenedNodes(t){const e=I(t);return Ln(t)?(t=t,e.assignedNodes({flatten:!0})):Array.from(e.childNodes).map((t=>Ln(t)?I(t=t).assignedNodes({flatten:!0}):[t])).reduce(((t,e)=>t.concat(e)),[])}constructor(t,e){this._shadyChildrenObserver=null,this._nativeChildrenObserver=null,this._connected=!1,this._target=t,this.callback=e,this._effectiveNodes=[],this._observer=null,this._scheduled=!1,this._boundSchedule=()=>{this._schedule()},this.connect(),this._schedule()}connect(){Ln(this._target)?this._listenSlots([this._target]):I(this._target).children&&(this._listenSlots(I(this._target).children),this._nativeChildrenObserver=new MutationObserver((t=>{this._processMutations(t)})),this._nativeChildrenObserver.observe(this._target,{childList:!0})),this._connected=!0}disconnect(){Ln(this._target)?this._unlistenSlots([this._target]):I(this._target).children&&(this._unlistenSlots(I(this._target).children),this._nativeChildrenObserver&&(this._nativeChildrenObserver.disconnect(),this._nativeChildrenObserver=null)),this._connected=!1}_schedule(){this._scheduled||(this._scheduled=!0,h.run((()=>this.flush())))}_processMutations(t){this._processSlotMutations(t),this.flush()}_processSlotMutations(t){if(t)for(let e=0;e<t.length;e++){let n=t[e];n.addedNodes&&this._listenSlots(n.addedNodes),n.removedNodes&&this._unlistenSlots(n.removedNodes)}}flush(){if(!this._connected)return!1;this._nativeChildrenObserver?this._processSlotMutations(this._nativeChildrenObserver.takeRecords()):this._shadyChildrenObserver&&this._processSlotMutations(this._shadyChildrenObserver.takeRecords()),this._scheduled=!1;let t={target:this._target,addedNodes:[],removedNodes:[]},e=this.constructor.getFlattenedNodes(this._target),n=In(e,this._effectiveNodes);for(let e,r=0;r<n.length&&(e=n[r]);r++)for(let n,r=0;r<e.removed.length&&(n=e.removed[r]);r++)t.removedNodes.push(n);for(let r,s=0;s<n.length&&(r=n[s]);s++)for(let n=r.index;n<r.index+r.addedCount;n++)t.addedNodes.push(e[n]);this._effectiveNodes=e;let r=!1;return(t.addedNodes.length||t.removedNodes.length)&&(r=!0,this.callback.call(this._target,t)),r}_listenSlots(t){for(let e=0;e<t.length;e++){let n=t[e];Ln(n)&&n.addEventListener("slotchange",this._boundSchedule)}}_unlistenSlots(t){for(let e=0;e<t.length;e++){let n=t[e];Ln(n)&&n.removeEventListener("slotchange",this._boundSchedule)}}};const Dn=Element.prototype,Rn=Dn.matches||Dn.matchesSelector||Dn.mozMatchesSelector||Dn.msMatchesSelector||Dn.oMatchesSelector||Dn.webkitMatchesSelector,Fn=function(t,e){return Rn.call(t,e)};class Hn{constructor(t){this.node=t}observeNodes(t){return new Mn(this.node,t)}unobserveNodes(t){t.disconnect()}notifyObserver(){}deepContains(t){if(I(this.node).contains(t))return!0;let e=t,n=t.ownerDocument;for(;e&&e!==n&&e!==this.node;)e=I(e).parentNode||I(e).host;return e===this.node}getOwnerRoot(){return I(this.node).getRootNode()}getDistributedNodes(){return"slot"===this.node.localName?I(this.node).assignedNodes({flatten:!0}):[]}getDestinationInsertionPoints(){let t=[],e=I(this.node).assignedSlot;for(;e;)t.push(e),e=I(e).assignedSlot;return t}importNode(t,e){let n=this.node instanceof Document?this.node:this.node.ownerDocument;return I(n).importNode(t,e)}getEffectiveChildNodes(){return Mn.getFlattenedNodes(this.node)}queryDistributedElements(t){let e=this.getEffectiveChildNodes(),n=[];for(let r,s=0,i=e.length;s<i&&(r=e[s]);s++)r.nodeType===Node.ELEMENT_NODE&&Fn(r,t)&&n.push(r);return n}get activeElement(){let t=this.node;return void 0!==t._activeElement?t._activeElement:t.activeElement}}function zn(t,e){for(let n=0;n<e.length;n++){let r=e[n];Object.defineProperty(t,r,{get:function(){return this.node[r]},configurable:!0})}}class jn{constructor(t){this.event=t}get rootTarget(){return this.path[0]}get localTarget(){return this.event.target}get path(){return this.event.composedPath()}}Hn.prototype.cloneNode,Hn.prototype.appendChild,Hn.prototype.insertBefore,Hn.prototype.removeChild,Hn.prototype.replaceChild,Hn.prototype.setAttribute,Hn.prototype.removeAttribute,Hn.prototype.querySelector,Hn.prototype.querySelectorAll,Hn.prototype.parentNode,Hn.prototype.firstChild,Hn.prototype.lastChild,Hn.prototype.nextSibling,Hn.prototype.previousSibling,Hn.prototype.firstElementChild,Hn.prototype.lastElementChild,Hn.prototype.nextElementSibling,Hn.prototype.previousElementSibling,Hn.prototype.childNodes,Hn.prototype.children,Hn.prototype.classList,Hn.prototype.textContent,Hn.prototype.innerHTML;let Bn=Hn;!function(t,e){for(let n=0;n<e.length;n++){let r=e[n];t[r]=function(){return this.node[r].apply(this.node,arguments)}}}(Hn.prototype,["cloneNode","appendChild","insertBefore","removeChild","replaceChild","setAttribute","removeAttribute","querySelector","querySelectorAll","attachShadow"]),zn(Hn.prototype,["parentNode","firstChild","lastChild","nextSibling","previousSibling","firstElementChild","lastElementChild","nextElementSibling","previousElementSibling","childNodes","children","classList","shadowRoot"]),function(t,e){for(let n=0;n<e.length;n++){let r=e[n];Object.defineProperty(t,r,{get:function(){return this.node[r]},set:function(t){this.node[r]=t},configurable:!0})}}(Hn.prototype,["textContent","innerHTML","className"]);const Jn=function(t){if((t=t||document)instanceof Bn)return t;if(t instanceof jn)return t;let e=t.__domApi;return e||(e=t instanceof Event?new jn(t):new Bn(t),t.__domApi=e),e};const qn=t=>{for(;t;){const e=Object.getOwnPropertyDescriptor(t,"observedAttributes");if(e)return e.get;t=Object.getPrototypeOf(t.prototype).constructor}return()=>[]};d((t=>{const e=Ye(t);let n=qn(e);return class extends e{constructor(){super(),this.__isUpgradeDisabled}static get observedAttributes(){return n.call(this).concat("disable-upgrade")}_initializeProperties(){this.hasAttribute("disable-upgrade")?this.__isUpgradeDisabled=!0:super._initializeProperties()}_enableProperties(){this.__isUpgradeDisabled||super._enableProperties()}_canApplyPropertyDefault(t){return super._canApplyPropertyDefault(t)&&!(this.__isUpgradeDisabled&&this._isPropertyPending(t))}attributeChangedCallback(t,e,n,r){"disable-upgrade"==t?this.__isUpgradeDisabled&&null==n&&(super._initializeProperties(),this.__isUpgradeDisabled=!1,I(this).isConnected&&super.connectedCallback()):super.attributeChangedCallback(t,e,n,r)}connectedCallback(){this.__isUpgradeDisabled||super.connectedCallback()}disconnectedCallback(){this.__isUpgradeDisabled||super.disconnectedCallback()}}}));const Yn=d((t=>{const e=Pn(Ye(t)),n=qn(e),r={x:"pan-x",y:"pan-y",none:"none",all:"auto"};class s extends e{constructor(){super(),this.isAttached,this.__boundListeners,this._debouncers,this.__isUpgradeDisabled,this.__needsAttributesAtConnected,this._legacyForceObservedAttributes}static get importMeta(){return this.prototype.importMeta}created(){}__attributeReaction(t,e,n){(this.__dataAttributes&&this.__dataAttributes[t]||"disable-upgrade"===t)&&this.attributeChangedCallback(t,e,n,null)}setAttribute(t,e){super.setAttribute(t,e)}removeAttribute(t){super.removeAttribute(t)}static get observedAttributes(){return n.call(this).concat("disable-upgrade")}_enableProperties(){this.__isUpgradeDisabled||super._enableProperties()}_canApplyPropertyDefault(t){return super._canApplyPropertyDefault(t)&&!(this.__isUpgradeDisabled&&this._isPropertyPending(t))}connectedCallback(){this.__needsAttributesAtConnected&&this._takeAttributes(),this.__isUpgradeDisabled||(super.connectedCallback(),this.isAttached=!0,this.attached())}attached(){}disconnectedCallback(){this.__isUpgradeDisabled||(super.disconnectedCallback(),this.isAttached=!1,this.detached())}detached(){}attributeChangedCallback(t,e,n,r){e!==n&&("disable-upgrade"==t?this.__isUpgradeDisabled&&null==n&&(this._initializeProperties(),this.__isUpgradeDisabled=!1,I(this).isConnected&&this.connectedCallback()):(super.attributeChangedCallback(t,e,n,r),this.attributeChanged(t,e,n)))}attributeChanged(t,e,n){}_initializeProperties(){if(x&&this.hasAttribute("disable-upgrade"))this.__isUpgradeDisabled=!0;else{let t=Object.getPrototypeOf(this);t.hasOwnProperty(JSCompiler_renameProperty("__hasRegisterFinished",t))||(this._registered(),t.__hasRegisterFinished=!0),super._initializeProperties(),this.root=this,this.created(),this._applyListeners()}}_takeAttributes(){const t=this.attributes;for(let e=0,n=t.length;e<n;e++){const n=t[e];this.__attributeReaction(n.name,null,n.value)}}_registered(){}ready(){this._ensureAttributes(),super.ready()}_ensureAttributes(){}_applyListeners(){}serialize(t){return this._serializeValue(t)}deserialize(t,e){return this._deserializeValue(t,e)}reflectPropertyToAttribute(t,e,n){this._propertyToAttribute(t,e,n)}serializeValueToAttribute(t,e,n){this._valueToNodeAttribute(n||this,t,e)}extend(t,e){if(!t||!e)return t||e;let n=Object.getOwnPropertyNames(e);for(let r,s=0;s<n.length&&(r=n[s]);s++){let n=Object.getOwnPropertyDescriptor(e,r);n&&Object.defineProperty(t,r,n)}return t}mixin(t,e){for(let n in e)t[n]=e[n];return t}chainObject(t,e){return t&&e&&t!==e&&(t.__proto__=e),t}instanceTemplate(t){let e=this.constructor._contentForTemplate(t);return document.importNode(e,!0)}fire(t,e,n){n=n||{},e=null==e?{}:e;let r=new Event(t,{bubbles:void 0===n.bubbles||n.bubbles,cancelable:Boolean(n.cancelable),composed:void 0===n.composed||n.composed});r.detail=e;let s=n.node||this;return I(s).dispatchEvent(r),r}listen(t,e,n){t=t||this;let r=this.__boundListeners||(this.__boundListeners=new WeakMap),s=r.get(t);s||(s={},r.set(t,s));let i=e+n;s[i]||(s[i]=this._addMethodEventListenerToNode(t,e,n,this))}unlisten(t,e,n){t=t||this;let r=this.__boundListeners&&this.__boundListeners.get(t),s=e+n,i=r&&r[s];i&&(this._removeEventListenerFromNode(t,e,i),r[s]=null)}setScrollDirection(t,e){lt(e||this,r[t]||"auto")}$$(t){return this.root.querySelector(t)}get domHost(){let t=I(this).getRootNode();return t instanceof DocumentFragment?t.host:t}distributeContent(){Jn(this)}getEffectiveChildNodes(){return Jn(this).getEffectiveChildNodes()}queryDistributedElements(t){return Jn(this).queryDistributedElements(t)}getEffectiveChildren(){return this.getEffectiveChildNodes().filter((function(t){return t.nodeType===Node.ELEMENT_NODE}))}getEffectiveTextContent(){let t=this.getEffectiveChildNodes(),e=[];for(let n,r=0;n=t[r];r++)n.nodeType!==Node.COMMENT_NODE&&e.push(n.textContent);return e.join("")}queryEffectiveChildren(t){let e=this.queryDistributedElements(t);return e&&e[0]}queryAllEffectiveChildren(t){return this.queryDistributedElements(t)}getContentChildNodes(t){let e=this.root.querySelector(t||"slot");return e?Jn(e).getDistributedNodes():[]}getContentChildren(t){return this.getContentChildNodes(t).filter((function(t){return t.nodeType===Node.ELEMENT_NODE}))}isLightDescendant(t){const e=this;return e!==t&&I(e).contains(t)&&I(e).getRootNode()===I(t).getRootNode()}isLocalDescendant(t){return this.root===I(t).getRootNode()}scopeSubtree(t,e=!1){return null}getComputedStyleValue(t){return false.getComputedStyleValue(this,t)}debounce(t,e,n){return this._debouncers=this._debouncers||{},this._debouncers[t]=_.debounce(this._debouncers[t],n>0?o.after(n):h,e.bind(this))}isDebouncerActive(t){this._debouncers=this._debouncers||{};let e=this._debouncers[t];return!(!e||!e.isActive())}flushDebouncer(t){this._debouncers=this._debouncers||{};let e=this._debouncers[t];e&&e.flush()}cancelDebouncer(t){this._debouncers=this._debouncers||{};let e=this._debouncers[t];e&&e.cancel()}async(t,e){return e>0?o.run(t.bind(this),e):~h.run(t.bind(this))}cancelAsync(t){t<0?h.cancel(~t):o.cancel(t)}create(t,e){let n=document.createElement(t);if(e)if(n.setProperties)n.setProperties(e);else for(let t in e)n[t]=e[t];return n}elementMatches(t,e){return Fn(e||this,t)}toggleAttribute(t,e){let n=this;return 3===arguments.length&&(n=arguments[2]),1==arguments.length&&(e=!n.hasAttribute(t)),e?(I(n).setAttribute(t,""),!0):(I(n).removeAttribute(t),!1)}toggleClass(t,e,n){n=n||this,1==arguments.length&&(e=!n.classList.contains(t)),e?n.classList.add(t):n.classList.remove(t)}transform(t,e){(e=e||this).style.webkitTransform=t,e.style.transform=t}translate3d(t,e,n,r){r=r||this,this.transform("translate3d("+t+","+e+","+n+")",r)}arrayDelete(t,e){let n;if(Array.isArray(t)){if(n=t.indexOf(e),n>=0)return t.splice(n,1)}else{if(n=zt(this,t).indexOf(e),n>=0)return this.splice(t,n,1)}return null}_logger(t,e){switch(Array.isArray(e)&&1===e.length&&Array.isArray(e[0])&&(e=e[0]),t){case"log":case"warn":case"error":console[t](...e)}}_log(...t){this._logger("log",t)}_warn(...t){this._logger("warn",t)}_error(...t){this._logger("error",t)}_logf(t,...e){return["[%s::%s]",this.is,t,...e]}}return s.prototype.is="",s})),Un={attached:!0,detached:!0,ready:!0,created:!0,beforeRegister:!0,registered:!0,attributeChanged:!0,listeners:!0,hostAttributes:!0},$n={attached:!0,detached:!0,ready:!0,created:!0,beforeRegister:!0,registered:!0,attributeChanged:!0,behaviors:!0,_noAccessors:!0},Vn=Object.assign({listeners:!0,hostAttributes:!0,properties:!0,observers:!0},$n);function Xn(t,e){return Qn({},Yn(e),t)}function Gn(t,e,n,r){!function(t,e,n){const r=t._noAccessors,s=Object.getOwnPropertyNames(t);for(let i=0;i<s.length;i++){let o=s[i];if(!(o in n))if(r)e[o]=t[o];else{let n=Object.getOwnPropertyDescriptor(t,o);n&&(n.configurable=!0,Object.defineProperty(e,o,n))}}}(e,t,r);for(let t in Un)e[t]&&(n[t]=n[t]||[],n[t].push(e[t]))}function Wn(t,e,n){e=e||[];for(let r=t.length-1;r>=0;r--){let s=t[r];s?Array.isArray(s)?Wn(s,e):e.indexOf(s)<0&&(!n||n.indexOf(s)<0)&&e.unshift(s):console.warn("behavior is null, check for missing or 404 import")}return e}function Zn(t,e){for(const n in e){const r=t[n],s=e[n];t[n]=!("value"in s)&&r&&"value"in r?Object.assign({value:r.value},s):s}}const Kn=Yn(HTMLElement);function Qn(t,e,n){let r;const s={};class i extends e{static _finalizeClass(){if(this.hasOwnProperty(JSCompiler_renameProperty("generatedFrom",this))){if(r)for(let t,e=0;e<r.length;e++)t=r[e],t.properties&&this.createProperties(t.properties),t.observers&&this.createObservers(t.observers,t.properties);t.properties&&this.createProperties(t.properties),t.observers&&this.createObservers(t.observers,t.properties),this._prepareTemplate()}else e._finalizeClass.call(this)}static get properties(){const e={};if(r)for(let t=0;t<r.length;t++)Zn(e,r[t].properties);return Zn(e,t.properties),e}static get observers(){let e=[];if(r)for(let t,n=0;n<r.length;n++)t=r[n],t.observers&&(e=e.concat(t.observers));return t.observers&&(e=e.concat(t.observers)),e}created(){super.created();const t=s.created;if(t)for(let e=0;e<t.length;e++)t[e].call(this)}_registered(){const t=i.prototype;if(!t.hasOwnProperty(JSCompiler_renameProperty("__hasRegisterFinished",t))){t.__hasRegisterFinished=!0,super._registered(),x&&o(t);const e=Object.getPrototypeOf(this);let n=s.beforeRegister;if(n)for(let t=0;t<n.length;t++)n[t].call(e);if(n=s.registered,n)for(let t=0;t<n.length;t++)n[t].call(e)}}_applyListeners(){super._applyListeners();const t=s.listeners;if(t)for(let e=0;e<t.length;e++){const n=t[e];if(n)for(let t in n)this._addMethodEventListenerToNode(this,t,n[t])}}_ensureAttributes(){const t=s.hostAttributes;if(t)for(let e=t.length-1;e>=0;e--){const n=t[e];for(let t in n)this._ensureAttribute(t,n[t])}super._ensureAttributes()}ready(){super.ready();let t=s.ready;if(t)for(let e=0;e<t.length;e++)t[e].call(this)}attached(){super.attached();let t=s.attached;if(t)for(let e=0;e<t.length;e++)t[e].call(this)}detached(){super.detached();let t=s.detached;if(t)for(let e=0;e<t.length;e++)t[e].call(this)}attributeChanged(t,e,n){super.attributeChanged();let r=s.attributeChanged;if(r)for(let s=0;s<r.length;s++)r[s].call(this,t,e,n)}}if(n){Array.isArray(n)||(n=[n]);let t=e.prototype.behaviors;r=Wn(n,null,t),i.prototype.behaviors=t?t.concat(n):r}const o=e=>{r&&function(t,e,n){for(let r=0;r<e.length;r++)Gn(t,e[r],n,Vn)}(e,r,s),Gn(e,t,s,$n)};return x||o(i.prototype),i.generatedFrom=t,i}let tr;tr=Qe._mutablePropertyChange;const er={properties:{mutableData:Boolean},_shouldPropertyChange(t,e,n){return tr(this,t,e,n,this.mutableData)}},nr=function(t){let e;return e="function"==typeof t?t:nr.Class(t),t._legacyForceObservedAttributes&&(e.prototype._legacyForceObservedAttributes=t._legacyForceObservedAttributes),customElements.define(e.is,e),e};nr.Class=function(t,e){t||console.warn("Polymer.Class requires `info` argument");let n=e?e(Kn):Kn;return n=Qn(t,n,t.behaviors),n.is=n.prototype.is=t.is,n};const rr={templatize(t,e){this._templatizerTemplate=t,this.ctor=pn(t,this,{mutableData:Boolean(e),parentModel:this._parentModel,instanceProps:this._instanceProps,forwardHostProp:this._forwardHostPropV2,notifyInstanceProp:this._notifyInstancePropV2})},stamp(t){return new this.ctor(t)},modelForElement(t){return fn(this._templatizerTemplate,t)}},sr=Pn(tn(Be(HTMLElement)));customElements.define("dom-bind",class extends sr{static get observedAttributes(){return["mutable-data"]}constructor(){if(super(),A)throw new Error("strictTemplatePolicy: dom-bind not allowed");this.root=null,this.$=null,this.__children=null}attributeChangedCallback(t,e,n,r){this.mutableData=!0}connectedCallback(){this.style.display="none",this.render()}disconnectedCallback(){this.__removeChildren()}__insertChildren(){I(I(this).parentNode).insertBefore(this.root,this)}__removeChildren(){if(this.__children)for(let t=0;t<this.__children.length;t++)this.root.appendChild(this.__children[t])}render(){let t;if(!this.__children){if(t=t||this.querySelector("template"),!t){let e=new MutationObserver((()=>{if(t=this.querySelector("template"),!t)throw new Error("dom-bind requires a <template> child");e.disconnect(),this.render()}));return void e.observe(this,{childList:!0})}this.root=this._stampTemplate(t),this.$=this.root.$,this.__children=[];for(let t=this.root.firstChild;t;t=t.nextSibling)this.__children[this.__children.length]=t;this._enableProperties()}this.__insertChildren(),this.dispatchEvent(new CustomEvent("dom-change",{bubbles:!0,composed:!0}))}});let ir=d((t=>{let e=Ye(t);return class extends e{static get properties(){return{items:{type:Array},multi:{type:Boolean,value:!1},selected:{type:Object,notify:!0},selectedItem:{type:Object,notify:!0},toggle:{type:Boolean,value:!1}}}static get observers(){return["__updateSelection(multi, items.*)"]}constructor(){super(),this.__lastItems=null,this.__lastMulti=null,this.__selectedMap=null}__updateSelection(t,e){let n=e.path;if(n==JSCompiler_renameProperty("items",this)){let n=e.base||[],r=this.__lastItems;if(t!==this.__lastMulti&&this.clearSelection(),r){let t=In(n,r);this.__applySplices(t)}this.__lastItems=n,this.__lastMulti=t}else if(e.path==JSCompiler_renameProperty("items",this)+".splices")this.__applySplices(e.value.indexSplices);else{let t=n.slice((JSCompiler_renameProperty("items",this)+".").length),e=parseInt(t,10);t.indexOf(".")<0&&t==e&&this.__deselectChangedIdx(e)}}__applySplices(t){let e=this.__selectedMap;for(let n=0;n<t.length;n++){let r=t[n];e.forEach(((t,n)=>{t<r.index||(t>=r.index+r.removed.length?e.set(n,t+r.addedCount-r.removed.length):e.set(n,-1))}));for(let t=0;t<r.addedCount;t++){let n=r.index+t;e.has(this.items[n])&&e.set(this.items[n],n)}}this.__updateLinks();let n=0;e.forEach(((t,r)=>{t<0?(this.multi?this.splice(JSCompiler_renameProperty("selected",this),n,1):this.selected=this.selectedItem=null,e.delete(r)):n++}))}__updateLinks(){if(this.__dataLinkedPaths={},this.multi){let t=0;this.__selectedMap.forEach((e=>{e>=0&&this.linkPaths(`${JSCompiler_renameProperty("items",this)}.${e}`,`${JSCompiler_renameProperty("selected",this)}.${t++}`)}))}else this.__selectedMap.forEach((t=>{this.linkPaths(JSCompiler_renameProperty("selected",this),`${JSCompiler_renameProperty("items",this)}.${t}`),this.linkPaths(JSCompiler_renameProperty("selectedItem",this),`${JSCompiler_renameProperty("items",this)}.${t}`)}))}clearSelection(){this.__dataLinkedPaths={},this.__selectedMap=new Map,this.selected=this.multi?[]:null,this.selectedItem=null}isSelected(t){return this.__selectedMap.has(t)}isIndexSelected(t){return this.isSelected(this.items[t])}__deselectChangedIdx(t){let e=this.__selectedIndexForItemIndex(t);if(e>=0){let t=0;this.__selectedMap.forEach(((n,r)=>{e==t++&&this.deselect(r)}))}}__selectedIndexForItemIndex(t){let e=this.__dataLinkedPaths[`${JSCompiler_renameProperty("items",this)}.${t}`];if(e)return parseInt(e.slice((JSCompiler_renameProperty("selected",this)+".").length),10)}deselect(t){let e=this.__selectedMap.get(t);if(e>=0){let n;this.__selectedMap.delete(t),this.multi&&(n=this.__selectedIndexForItemIndex(e)),this.__updateLinks(),this.multi?this.splice(JSCompiler_renameProperty("selected",this),n,1):this.selected=this.selectedItem=null}}deselectIndex(t){this.deselect(this.items[t])}select(t){this.selectIndex(this.items.indexOf(t))}selectIndex(t){let e=this.items[t];this.isSelected(e)?this.toggle&&this.deselectIndex(t):(this.multi||this.__selectedMap.clear(),this.__selectedMap.set(e,t),this.__updateLinks(),this.multi?this.push(JSCompiler_renameProperty("selected",this),e):this.selected=this.selectedItem=e)}}}))(We);class or extends ir{static get is(){return"array-selector"}static get template(){return null}}customElements.define(or.is,or);class ar extends HTMLElement{constructor(){super(),this._style=null}getStyle(){if(this._style)return this._style;const t=this.querySelector("style");if(!t)return null;this._style=t;const e=t.getAttribute("include");return e&&(t.removeAttribute("include"),t.textContent=function(t){let e=t.trim().split(/\s+/),n="";for(let t=0;t<e.length;t++)n+=St(e[t]);return n}(e)+t.textContent),this.ownerDocument!==window.document&&window.document.head.appendChild(this),this._style}}window.customElements.define("custom-style",ar);const lr=Yn(HTMLElement).prototype;export{lr as Base,_ as Debouncer,yn as DomIf,bn as DomRepeat,Mn as FlattenedNodesObserver,er as OptionalMutableDataBehavior,nr as Polymer,We as PolymerElement,an as TemplateInstanceBase,rr as Templatizer,An as afterNextRender,a as animationFrame,On as beforeNextRender,In as calculateSplices,Yt as dashToCamelCase,d as dedupingMixin,Jn as dom,p as enqueueDebouncer,Ze as flush,gt as gestures,zt as get,Xe as html,l as idlePeriod,Rt as matches,h as microTask,Xn as mixinBehaviors,pn as templatize,o as timeOut,Dt as translate,w as useShadow}; \ No newline at end of file +window.JSCompiler_renameProperty=function(t,e){return t};let t=0,e=0,n=[],r=0,s=!1,i=document.createTextNode("");new window.MutationObserver((function(){s=!1;const t=n.length;for(let e=0;e<t;e++){let t=n[e];if(t)try{t()}catch(t){setTimeout((()=>{throw t}))}}n.splice(0,t),e+=t})).observe(i,{characterData:!0});const o={after:t=>({run:e=>window.setTimeout(e,t),cancel(t){window.clearTimeout(t)}}),run:(t,e)=>window.setTimeout(t,e),cancel(t){window.clearTimeout(t)}},a={run:t=>window.requestAnimationFrame(t),cancel(t){window.cancelAnimationFrame(t)}},l={run:t=>window.requestIdleCallback?window.requestIdleCallback(t):window.setTimeout(t,16),cancel(t){window.cancelIdleCallback?window.cancelIdleCallback(t):window.clearTimeout(t)}},h={run:e=>(s||(s=!0,i.textContent=r++),n.push(e),t++),cancel(t){const r=t-e;if(r>=0){if(!n[r])throw new Error("invalid async handle: "+t);n[r]=null}}};let c=0;const d=function(t){let e=t.__mixinApplications;e||(e=new WeakMap,t.__mixinApplications=e);let n=c++;return function(r){let s=r.__mixinSet;if(s&&s[n])return r;let i=e,o=i.get(r);if(!o){o=t(r),i.set(r,o);let e=Object.create(o.__mixinSet||s||null);e[n]=!0,o.__mixinSet=e}return o}};class _{constructor(){this._asyncModule=null,this._callback=null,this._timer=null}setConfig(t,e){this._asyncModule=t,this._callback=e,this._timer=this._asyncModule.run((()=>{this._timer=null,u.delete(this),this._callback()}))}cancel(){this.isActive()&&(this._cancelAsync(),u.delete(this))}_cancelAsync(){this.isActive()&&(this._asyncModule.cancel(this._timer),this._timer=null)}flush(){this.isActive()&&(this.cancel(),this._callback())}isActive(){return null!=this._timer}static debounce(t,e,n){return t instanceof _?t._cancelAsync():t=new _,t.setConfig(e,n),t}}let u=new Set;const p=function(t){u.add(t)},f=function(){const t=Boolean(u.size);return u.forEach((t=>{try{t.flush()}catch(t){setTimeout((()=>{throw t}))}})),t};let m,y,g=/(url\()([^)]*)(\))/g,b=/(^\/[^\/])|(^#)|(^[\w-\d]*:)/;function P(t,e){if(t&&b.test(t))return t;if("//"===t)return t;if(void 0===m){m=!1;try{const t=new URL("b","http://a");t.pathname="c%20d",m="http://a/c%20d"===t.href}catch(t){}}if(e||(e=document.baseURI||window.location.href),m)try{return new URL(t,e).href}catch(e){return t}return y||(y=document.implementation.createHTMLDocument("temp"),y.base=y.createElement("base"),y.head.appendChild(y.base),y.anchor=y.createElement("a"),y.body.appendChild(y.anchor)),y.base.href=e,y.anchor.href=t,y.anchor.href||t}function v(t,e){return t.replace(g,(function(t,n,r,s){return n+"'"+P(r.replace(/["']/g,""),e)+"'"+s}))}function C(t){return t.substring(0,t.lastIndexOf("/")+1)}const w=!0;Boolean(!0),"adoptedStyleSheets"in Document.prototype&&"replaceSync"in CSSStyleSheet.prototype&&(()=>{try{const t=new CSSStyleSheet;t.replaceSync("");const e=document.createElement("div");e.attachShadow({mode:"open"}),e.shadowRoot.adoptedStyleSheets=[t],e.shadowRoot.adoptedStyleSheets[0]}catch(t){return!1}})();let E=window.Polymer&&window.Polymer.rootPath||C(document.baseURI||window.location.href),T=window.Polymer&&window.Polymer.sanitizeDOMValue||void 0,O=window.Polymer&&window.Polymer.setPassiveTouchGestures||!1,A=window.Polymer&&window.Polymer.strictTemplatePolicy||!1,N=window.Polymer&&window.Polymer.allowTemplateFromDomModule||!1,x=(window.Polymer,!1),S=window.Polymer&&window.Polymer.orderedComputed||!1;const I=t=>t;let k="string"==typeof document.head.style.touchAction,L="__polymerGestures",M="__polymerGesturesHandled",D="__polymerGesturesTouchAction",R=["mousedown","mousemove","mouseup","click"],F=[0,1,4,2],H=function(){try{return 1===new MouseEvent("test",{buttons:1}).buttons}catch(t){return!1}}();function z(t){return R.indexOf(t)>-1}let j=!1;function B(t){if(!z(t)&&"touchend"!==t)return k&&j&&O?{passive:!0}:void 0}!function(){try{let t=Object.defineProperty({},"passive",{get(){j=!0}});window.addEventListener("test",null,t),window.removeEventListener("test",null,t)}catch(t){}}();let J=navigator.userAgent.match(/iP(?:[oa]d|hone)|Android/);const q=[],Y={button:!0,input:!0,keygen:!0,meter:!0,output:!0,textarea:!0,progress:!0,select:!0},$={button:!0,command:!0,fieldset:!0,input:!0,keygen:!0,optgroup:!0,option:!0,select:!0,textarea:!0};function U(t){let e=Array.prototype.slice.call(t.labels||[]);if(!e.length){e=[];try{let n=t.getRootNode();if(t.id){let r=n.querySelectorAll(`label[for = '${t.id}']`);for(let t=0;t<r.length;t++)e.push(r[t])}}catch(t){}}return e}let V=function(t){let e=t.sourceCapabilities;var n;if((!e||e.firesTouchEvents)&&(t[M]={skip:!0},"click"===t.type)){let e=!1,r=Q(t);for(let t=0;t<r.length;t++){if(r[t].nodeType===Node.ELEMENT_NODE)if("label"===r[t].localName)q.push(r[t]);else if(n=r[t],Y[n.localName]){let n=U(r[t]);for(let t=0;t<n.length;t++)e=e||q.indexOf(n[t])>-1}if(r[t]===W.mouse.target)return}if(e)return;t.preventDefault(),t.stopPropagation()}};function X(t){let e=J?["click"]:R;for(let n,r=0;r<e.length;r++)n=e[r],t?(q.length=0,document.addEventListener(n,V,!0)):document.removeEventListener(n,V,!0)}function G(t){let e=t.type;if(!z(e))return!1;if("mousemove"===e){let e=void 0===t.buttons?1:t.buttons;return t instanceof window.MouseEvent&&!H&&(e=F[t.which]||0),Boolean(1&e)}return 0===(void 0===t.button?0:t.button)}let W={mouse:{target:null,mouseIgnoreJob:null},touch:{x:0,y:0,id:-1,scrollDecided:!1}};function Z(t,e,n){t.movefn=e,t.upfn=n,document.addEventListener("mousemove",e),document.addEventListener("mouseup",n)}function K(t){document.removeEventListener("mousemove",t.movefn),document.removeEventListener("mouseup",t.upfn),t.movefn=null,t.upfn=null}document.addEventListener("touchend",(function(t){W.mouse.mouseIgnoreJob||X(!0),W.mouse.target=Q(t)[0],W.mouse.mouseIgnoreJob=_.debounce(W.mouse.mouseIgnoreJob,o.after(2500),(function(){X(),W.mouse.target=null,W.mouse.mouseIgnoreJob=null}))}),!!j&&{passive:!0});const Q=t=>t.composedPath&&t.composedPath()||[],tt={},et=[];function nt(t,e){let n=document.elementFromPoint(t,e),r=n;for(;r&&r.shadowRoot;){let s=r;if(r=r.shadowRoot.elementFromPoint(t,e),s===r)break;r&&(n=r)}return n}function rt(t){const e=Q(t);return e.length>0?e[0]:t.target}function st(t){let e,n=t.type,r=t.currentTarget[L];if(!r)return;let s=r[n];if(s){if(!t[M]&&(t[M]={},"touch"===n.slice(0,5))){let e=t.changedTouches[0];if("touchstart"===n&&1===t.touches.length&&(W.touch.id=e.identifier),W.touch.id!==e.identifier)return;k||"touchstart"!==n&&"touchmove"!==n||function(t){let e=t.changedTouches[0],n=t.type;if("touchstart"===n)W.touch.x=e.clientX,W.touch.y=e.clientY,W.touch.scrollDecided=!1;else if("touchmove"===n){if(W.touch.scrollDecided)return;W.touch.scrollDecided=!0;let n=function(t){let e="auto",n=Q(t);for(let t,r=0;r<n.length;r++)if(t=n[r],t[D]){e=t[D];break}return e}(t),r=!1,s=Math.abs(W.touch.x-e.clientX),i=Math.abs(W.touch.y-e.clientY);t.cancelable&&("none"===n?r=!0:"pan-x"===n?r=i>s:"pan-y"===n&&(r=s>i)),r?t.preventDefault():ct("track")}}(t)}if(e=t[M],!e.skip){for(let n,r=0;r<et.length;r++)n=et[r],s[n.name]&&!e[n.name]&&n.flow&&n.flow.start.indexOf(t.type)>-1&&n.reset&&n.reset();for(let r,i=0;i<et.length;i++)r=et[i],s[r.name]&&!e[r.name]&&(e[r.name]=!0,r[n](t))}}}function it(t,e,n){return!!tt[e]&&(function(t,e,n){let r=tt[e],s=r.deps,i=r.name,o=t[L];o||(t[L]=o={});for(let e,n,r=0;r<s.length;r++)e=s[r],J&&z(e)&&"click"!==e||(n=o[e],n||(o[e]=n={_count:0}),0===n._count&&t.addEventListener(e,st,B(e)),n[i]=(n[i]||0)+1,n._count=(n._count||0)+1);t.addEventListener(e,n),r.touchAction&<(t,r.touchAction)}(t,e,n),!0)}function ot(t,e,n){return!!tt[e]&&(function(t,e,n){let r=tt[e],s=r.deps,i=r.name,o=t[L];if(o)for(let e,n,r=0;r<s.length;r++)e=s[r],n=o[e],n&&n[i]&&(n[i]=(n[i]||1)-1,n._count=(n._count||1)-1,0===n._count&&t.removeEventListener(e,st,B(e)));t.removeEventListener(e,n)}(t,e,n),!0)}function at(t){et.push(t);for(let e=0;e<t.emits.length;e++)tt[t.emits[e]]=t}function lt(t,e){k&&t instanceof HTMLElement&&h.run((()=>{t.style.touchAction=e})),t[D]=e}function ht(t,e,n){let r=new Event(e,{bubbles:!0,cancelable:!0,composed:!0});if(r.detail=n,I(t).dispatchEvent(r),r.defaultPrevented){let t=n.preventer||n.sourceEvent;t&&t.preventDefault&&t.preventDefault()}}function ct(t){let e=function(t){for(let e,n=0;n<et.length;n++){e=et[n];for(let n,r=0;r<e.emits.length;r++)if(n=e.emits[r],n===t)return e}return null}(t);e.info&&(e.info.prevent=!0)}function dt(t,e,n,r){e&&ht(e,t,{x:n.clientX,y:n.clientY,sourceEvent:n,preventer:r,prevent:function(t){return ct(t)}})}function _t(t,e,n){if(t.prevent)return!1;if(t.started)return!0;let r=Math.abs(t.x-e),s=Math.abs(t.y-n);return r>=5||s>=5}function ut(t,e,n){if(!e)return;let r,s=t.moves[t.moves.length-2],i=t.moves[t.moves.length-1],o=i.x-t.x,a=i.y-t.y,l=0;s&&(r=i.x-s.x,l=i.y-s.y),ht(e,"track",{state:t.state,x:n.clientX,y:n.clientY,dx:o,dy:a,ddx:r,ddy:l,sourceEvent:n,hover:function(){return nt(n.clientX,n.clientY)}})}function pt(t,e,n){let r=Math.abs(e.clientX-t.x),s=Math.abs(e.clientY-t.y),i=rt(n||e);!i||$[i.localName]&&i.hasAttribute("disabled")||(isNaN(r)||isNaN(s)||r<=25&&s<=25||function(t){if("click"===t.type){if(0===t.detail)return!0;let e=rt(t);if(!e.nodeType||e.nodeType!==Node.ELEMENT_NODE)return!0;let n=e.getBoundingClientRect(),r=t.pageX,s=t.pageY;return!(r>=n.left&&r<=n.right&&s>=n.top&&s<=n.bottom)}return!1}(e))&&(t.prevent||ht(i,"tap",{x:e.clientX,y:e.clientY,sourceEvent:e,preventer:n}))}at({name:"downup",deps:["mousedown","touchstart","touchend"],flow:{start:["mousedown","touchstart"],end:["mouseup","touchend"]},emits:["down","up"],info:{movefn:null,upfn:null},reset:function(){K(this.info)},mousedown:function(t){if(!G(t))return;let e=rt(t),n=this;Z(this.info,(function(t){G(t)||(dt("up",e,t),K(n.info))}),(function(t){G(t)&&dt("up",e,t),K(n.info)})),dt("down",e,t)},touchstart:function(t){dt("down",rt(t),t.changedTouches[0],t)},touchend:function(t){dt("up",rt(t),t.changedTouches[0],t)}}),at({name:"track",touchAction:"none",deps:["mousedown","touchstart","touchmove","touchend"],flow:{start:["mousedown","touchstart"],end:["mouseup","touchend"]},emits:["track"],info:{x:0,y:0,state:"start",started:!1,moves:[],addMove:function(t){this.moves.length>2&&this.moves.shift(),this.moves.push(t)},movefn:null,upfn:null,prevent:!1},reset:function(){this.info.state="start",this.info.started=!1,this.info.moves=[],this.info.x=0,this.info.y=0,this.info.prevent=!1,K(this.info)},mousedown:function(t){if(!G(t))return;let e=rt(t),n=this,r=function(t){let r=t.clientX,s=t.clientY;_t(n.info,r,s)&&(n.info.state=n.info.started?"mouseup"===t.type?"end":"track":"start","start"===n.info.state&&ct("tap"),n.info.addMove({x:r,y:s}),G(t)||(n.info.state="end",K(n.info)),e&&ut(n.info,e,t),n.info.started=!0)};Z(this.info,r,(function(t){n.info.started&&r(t),K(n.info)})),this.info.x=t.clientX,this.info.y=t.clientY},touchstart:function(t){let e=t.changedTouches[0];this.info.x=e.clientX,this.info.y=e.clientY},touchmove:function(t){let e=rt(t),n=t.changedTouches[0],r=n.clientX,s=n.clientY;_t(this.info,r,s)&&("start"===this.info.state&&ct("tap"),this.info.addMove({x:r,y:s}),ut(this.info,e,n),this.info.state="track",this.info.started=!0)},touchend:function(t){let e=rt(t),n=t.changedTouches[0];this.info.started&&(this.info.state="end",this.info.addMove({x:n.clientX,y:n.clientY}),ut(this.info,e,n))}}),at({name:"tap",deps:["mousedown","click","touchstart","touchend"],flow:{start:["mousedown","touchstart"],end:["click","touchend"]},emits:["tap"],info:{x:NaN,y:NaN,prevent:!1},reset:function(){this.info.x=NaN,this.info.y=NaN,this.info.prevent=!1},mousedown:function(t){G(t)&&(this.info.x=t.clientX,this.info.y=t.clientY)},click:function(t){G(t)&&pt(this.info,t)},touchstart:function(t){const e=t.changedTouches[0];this.info.x=e.clientX,this.info.y=e.clientY},touchend:function(t){pt(this.info,t.changedTouches[0],t)}});const ft=rt,mt=it,yt=ot;var gt=Object.freeze({__proto__:null,add:mt,addListener:it,deepTargetFind:nt,findOriginalTarget:ft,gestures:tt,prevent:ct,recognizers:et,register:at,remove:yt,removeListener:ot,resetMouseCanceller:function(){W.mouse.mouseIgnoreJob&&W.mouse.mouseIgnoreJob.flush()},setTouchAction:lt});let bt={},Pt={};function vt(t,e){bt[t]=Pt[t.toLowerCase()]=e}function Ct(t){return bt[t]||Pt[t.toLowerCase()]}class wt extends HTMLElement{static get observedAttributes(){return["id"]}static import(t,e){if(t){let n=Ct(t);return n&&e?n.querySelector(e):n}return null}attributeChangedCallback(t,e,n,r){e!==n&&this.register()}get assetpath(){if(!this.__assetpath){const t=this.ownerDocument,e=P(this.getAttribute("assetpath")||"",t.baseURI);this.__assetpath=C(e)}return this.__assetpath}register(t){if(t=t||this.id){if(A&&void 0!==Ct(t))throw vt(t,null),new Error(`strictTemplatePolicy: dom-module ${t} re-registered`);this.id=t,vt(t,this),(e=this).querySelector("style")&&console.warn("dom-module %s has style outside template",e.id)}var e}}wt.prototype.modules=bt,customElements.define("dom-module",wt);const Et="shady-unscoped";function Tt(t){return wt.import(t)}function Ot(t){const e=v((t.body?t.body:t).textContent,t.baseURI),n=document.createElement("style");return n.textContent=e,n}function At(t){const e=t.trim().split(/\s+/),n=[];for(let t=0;t<e.length;t++)n.push(...Nt(e[t]));return n}function Nt(t){const e=Tt(t);if(!e)return console.warn("Could not find style data in module named",t),[];if(void 0===e._styles){const t=[];t.push(...St(e));const n=e.querySelector("template");n&&t.push(...xt(n,e.assetpath)),e._styles=t}return e._styles}function xt(t,e){if(!t._styles){const n=[],r=t.content.querySelectorAll("style");for(let t=0;t<r.length;t++){let s=r[t],i=s.getAttribute("include");i&&n.push(...At(i).filter((function(t,e,n){return n.indexOf(t)===e}))),e&&(s.textContent=v(s.textContent,e)),n.push(s)}t._styles=n}return t._styles}function St(t){const e=[],n=t.querySelectorAll("link[rel=import][type~=css]");for(let t=0;t<n.length;t++){let r=n[t];if(r.import){const t=r.import,n=r.hasAttribute(Et);if(n&&!t._unscopedStyle){const e=Ot(t);e.setAttribute(Et,""),t._unscopedStyle=e}else t._style||(t._style=Ot(t));e.push(n?t._unscopedStyle:t._style)}}return e}function It(t){let e=Tt(t);if(e&&void 0===e._cssText){let t=function(t){let e="",n=St(t);for(let t=0;t<n.length;t++)e+=n[t].textContent;return e}(e),n=e.querySelector("template");n&&(t+=function(t,e){let n="";const r=xt(t,e);for(let t=0;t<r.length;t++){let e=r[t];e.parentNode&&e.parentNode.removeChild(e),n+=e.textContent}return n}(n,e.assetpath)),e._cssText=t||null}return e||console.warn("Could not find style data in module named",t),e&&e._cssText||""}function kt(t){return t.indexOf(".")>=0}function Lt(t){let e=t.indexOf(".");return-1===e?t:t.slice(0,e)}function Mt(t,e){return 0===t.indexOf(e+".")}function Dt(t,e){return 0===e.indexOf(t+".")}function Rt(t,e,n){return e+n.slice(t.length)}function Ft(t,e){return t===e||Mt(t,e)||Dt(t,e)}function Ht(t){if(Array.isArray(t)){let e=[];for(let n=0;n<t.length;n++){let r=t[n].toString().split(".");for(let t=0;t<r.length;t++)e.push(r[t])}return e.join(".")}return t}function zt(t){return Array.isArray(t)?Ht(t).split("."):t.toString().split(".")}function jt(t,e,n){let r=t,s=zt(e);for(let t=0;t<s.length;t++){if(!r)return;r=r[s[t]]}return n&&(n.path=s.join(".")),r}function Bt(t,e,n){let r=t,s=zt(e),i=s[s.length-1];if(s.length>1){for(let t=0;t<s.length-1;t++){if(r=r[s[t]],!r)return}r[i]=n}else r[e]=n;return s.join(".")}const Jt={},qt=/-[a-z]/g,Yt=/([A-Z])/g;function $t(t){return Jt[t]||(Jt[t]=t.indexOf("-")<0?t:t.replace(qt,(t=>t[1].toUpperCase())))}function Ut(t){return Jt[t]||(Jt[t]=t.replace(Yt,"-$1").toLowerCase())}const Vt=h,Xt=d((t=>class extends t{static createProperties(t){const e=this.prototype;for(let n in t)n in e||e._createPropertyAccessor(n)}static attributeNameForProperty(t){return t.toLowerCase()}static typeForProperty(t){}_createPropertyAccessor(t,e){this._addPropertyToAttributeMap(t),this.hasOwnProperty(JSCompiler_renameProperty("__dataHasAccessor",this))||(this.__dataHasAccessor=Object.assign({},this.__dataHasAccessor)),this.__dataHasAccessor[t]||(this.__dataHasAccessor[t]=!0,this._definePropertyAccessor(t,e))}_addPropertyToAttributeMap(t){this.hasOwnProperty(JSCompiler_renameProperty("__dataAttributes",this))||(this.__dataAttributes=Object.assign({},this.__dataAttributes));let e=this.__dataAttributes[t];return e||(e=this.constructor.attributeNameForProperty(t),this.__dataAttributes[e]=t),e}_definePropertyAccessor(t,e){Object.defineProperty(this,t,{get(){return this.__data[t]},set:e?function(){}:function(e){this._setPendingProperty(t,e,!0)&&this._invalidateProperties()}})}constructor(){super(),this.__dataEnabled=!1,this.__dataReady=!1,this.__dataInvalid=!1,this.__data={},this.__dataPending=null,this.__dataOld=null,this.__dataInstanceProps=null,this.__dataCounter=0,this.__serializing=!1,this._initializeProperties()}ready(){this.__dataReady=!0,this._flushProperties()}_initializeProperties(){for(let t in this.__dataHasAccessor)this.hasOwnProperty(t)&&(this.__dataInstanceProps=this.__dataInstanceProps||{},this.__dataInstanceProps[t]=this[t],delete this[t])}_initializeInstanceProperties(t){Object.assign(this,t)}_setProperty(t,e){this._setPendingProperty(t,e)&&this._invalidateProperties()}_getProperty(t){return this.__data[t]}_setPendingProperty(t,e,n){let r=this.__data[t],s=this._shouldPropertyChange(t,e,r);return s&&(this.__dataPending||(this.__dataPending={},this.__dataOld={}),this.__dataOld&&!(t in this.__dataOld)&&(this.__dataOld[t]=r),this.__data[t]=e,this.__dataPending[t]=e),s}_isPropertyPending(t){return!(!this.__dataPending||!this.__dataPending.hasOwnProperty(t))}_invalidateProperties(){!this.__dataInvalid&&this.__dataReady&&(this.__dataInvalid=!0,Vt.run((()=>{this.__dataInvalid&&(this.__dataInvalid=!1,this._flushProperties())})))}_enableProperties(){this.__dataEnabled||(this.__dataEnabled=!0,this.__dataInstanceProps&&(this._initializeInstanceProperties(this.__dataInstanceProps),this.__dataInstanceProps=null),this.ready())}_flushProperties(){this.__dataCounter++;const t=this.__data,e=this.__dataPending,n=this.__dataOld;this._shouldPropertiesChange(t,e,n)&&(this.__dataPending=null,this.__dataOld=null,this._propertiesChanged(t,e,n)),this.__dataCounter--}_shouldPropertiesChange(t,e,n){return Boolean(e)}_propertiesChanged(t,e,n){}_shouldPropertyChange(t,e,n){return n!==e&&(n==n||e==e)}attributeChangedCallback(t,e,n,r){e!==n&&this._attributeToProperty(t,n),super.attributeChangedCallback&&super.attributeChangedCallback(t,e,n,r)}_attributeToProperty(t,e,n){if(!this.__serializing){const r=this.__dataAttributes,s=r&&r[t]||t;this[s]=this._deserializeValue(e,n||this.constructor.typeForProperty(s))}}_propertyToAttribute(t,e,n){this.__serializing=!0,n=arguments.length<3?this[t]:n,this._valueToNodeAttribute(this,n,e||this.constructor.attributeNameForProperty(t)),this.__serializing=!1}_valueToNodeAttribute(t,e,n){const r=this._serializeValue(e);"class"!==n&&"name"!==n&&"slot"!==n||(t=I(t)),void 0===r?t.removeAttribute(n):t.setAttribute(n,r)}_serializeValue(t){return"boolean"==typeof t?t?"":void 0:null!=t?t.toString():void 0}_deserializeValue(t,e){switch(e){case Boolean:return null!==t;case Number:return Number(t);default:return t}}})),Gt={};let Wt=HTMLElement.prototype;for(;Wt;){let t=Object.getOwnPropertyNames(Wt);for(let e=0;e<t.length;e++)Gt[t[e]]=!0;Wt=Object.getPrototypeOf(Wt)}const Zt=window.trustedTypes?t=>trustedTypes.isHTML(t)||trustedTypes.isScript(t)||trustedTypes.isScriptURL(t):()=>!1;const Kt=d((t=>{const e=Xt(t);return class extends e{static createPropertiesForAttributes(){let t=this.observedAttributes;for(let e=0;e<t.length;e++)this.prototype._createPropertyAccessor($t(t[e]))}static attributeNameForProperty(t){return Ut(t)}_initializeProperties(){this.__dataProto&&(this._initializeProtoProperties(this.__dataProto),this.__dataProto=null),super._initializeProperties()}_initializeProtoProperties(t){for(let e in t)this._setProperty(e,t[e])}_ensureAttribute(t,e){const n=this;n.hasAttribute(t)||this._valueToNodeAttribute(n,e,t)}_serializeValue(t){if("object"==typeof t){if(t instanceof Date)return t.toString();if(t){if(Zt(t))return t;try{return JSON.stringify(t)}catch(t){return""}}}return super._serializeValue(t)}_deserializeValue(t,e){let n;switch(e){case Object:try{n=JSON.parse(t)}catch(e){n=t}break;case Array:try{n=JSON.parse(t)}catch(e){n=null,console.warn(`Polymer::Attributes: couldn't decode Array as JSON: ${t}`)}break;case Date:n=isNaN(t)?String(t):Number(t),n=new Date(n);break;default:n=super._deserializeValue(t,e)}return n}_definePropertyAccessor(t,e){!function(t,e){if(!Gt[e]){let n=t[e];void 0!==n&&(t.__data?t._setPendingProperty(e,n):(t.__dataProto?t.hasOwnProperty(JSCompiler_renameProperty("__dataProto",t))||(t.__dataProto=Object.create(t.__dataProto)):t.__dataProto={},t.__dataProto[e]=n))}}(this,t),super._definePropertyAccessor(t,e)}_hasAccessor(t){return this.__dataHasAccessor&&this.__dataHasAccessor[t]}_isPropertyPending(t){return Boolean(this.__dataPending&&t in this.__dataPending)}}})),Qt={"dom-if":!0,"dom-repeat":!0};let te=!1,ee=!1;function ne(t){(function(){if(!te){te=!0;const t=document.createElement("textarea");t.placeholder="a",ee=t.placeholder===t.textContent}return ee})()&&"textarea"===t.localName&&t.placeholder&&t.placeholder===t.textContent&&(t.textContent=null)}const re=(()=>{const t=window.trustedTypes&&window.trustedTypes.createPolicy("polymer-template-event-attribute-policy",{createScript:t=>t});return(e,n,r)=>{const s=n.getAttribute(r);t&&r.startsWith("on-")?e.setAttribute(r,t.createScript(s,r)):e.setAttribute(r,s)}})();function se(t){let e=t.getAttribute("is");if(e&&Qt[e]){let n=t;for(n.removeAttribute("is"),t=n.ownerDocument.createElement(e),n.parentNode.replaceChild(t,n),t.appendChild(n);n.attributes.length;){const{name:e}=n.attributes[0];re(t,n,e),n.removeAttribute(e)}}return t}function ie(t,e){let n=e.parentInfo&&ie(t,e.parentInfo);if(!n)return t;for(let t=n.firstChild,r=0;t;t=t.nextSibling)if(e.parentIndex===r++)return t}function oe(t,e,n,r){r.id&&(e[r.id]=n)}function ae(t,e,n){if(n.events&&n.events.length)for(let r,s=0,i=n.events;s<i.length&&(r=i[s]);s++)t._addMethodEventListenerToNode(e,r.name,r.value,t)}function le(t,e,n,r){n.templateInfo&&(e._templateInfo=n.templateInfo,e._parentTemplateInfo=r)}const he=d((t=>class extends t{static _parseTemplate(t,e){if(!t._templateInfo){let n=t._templateInfo={};n.nodeInfoList=[],n.nestedTemplate=Boolean(e),n.stripWhiteSpace=!0,this._parseTemplateContent(t,n,{parent:null})}return t._templateInfo}static _parseTemplateContent(t,e,n){return this._parseTemplateNode(t.content,e,n)}static _parseTemplateNode(t,e,n){let r=!1,s=t;return"template"!=s.localName||s.hasAttribute("preserve-content")?"slot"===s.localName&&(e.hasInsertionPoint=!0):r=this._parseTemplateNestedTemplate(s,e,n)||r,ne(s),s.firstChild&&this._parseTemplateChildNodes(s,e,n),s.hasAttributes&&s.hasAttributes()&&(r=this._parseTemplateNodeAttributes(s,e,n)||r),r||n.noted}static _parseTemplateChildNodes(t,e,n){if("script"!==t.localName&&"style"!==t.localName)for(let r,s=t.firstChild,i=0;s;s=r){if("template"==s.localName&&(s=se(s)),r=s.nextSibling,s.nodeType===Node.TEXT_NODE){let n=r;for(;n&&n.nodeType===Node.TEXT_NODE;)s.textContent+=n.textContent,r=n.nextSibling,t.removeChild(n),n=r;if(e.stripWhiteSpace&&!s.textContent.trim()){t.removeChild(s);continue}}let o={parentIndex:i,parentInfo:n};this._parseTemplateNode(s,e,o)&&(o.infoIndex=e.nodeInfoList.push(o)-1),s.parentNode&&i++}}static _parseTemplateNestedTemplate(t,e,n){let r=t,s=this._parseTemplate(r,e);return(s.content=r.content.ownerDocument.createDocumentFragment()).appendChild(r.content),n.templateInfo=s,!0}static _parseTemplateNodeAttributes(t,e,n){let r=!1,s=Array.from(t.attributes);for(let i,o=s.length-1;i=s[o];o--)r=this._parseTemplateNodeAttribute(t,e,n,i.name,i.value)||r;return r}static _parseTemplateNodeAttribute(t,e,n,r,s){return"on-"===r.slice(0,3)?(t.removeAttribute(r),n.events=n.events||[],n.events.push({name:r.slice(3),value:s}),!0):"id"===r&&(n.id=s,!0)}static _contentForTemplate(t){let e=t._templateInfo;return e&&e.content||t.content}_stampTemplate(t,e){t&&!t.content&&window.HTMLTemplateElement&&HTMLTemplateElement.decorate&&HTMLTemplateElement.decorate(t);let n=(e=e||this.constructor._parseTemplate(t)).nodeInfoList,r=e.content||t.content,s=document.importNode(r,!0);s.__noInsertionPoint=!e.hasInsertionPoint;let i=s.nodeList=new Array(n.length);s.$={};for(let t,r=0,o=n.length;r<o&&(t=n[r]);r++){let n=i[r]=ie(s,t);oe(0,s.$,n,t),le(0,n,t,e),ae(this,n,t)}return s}_addMethodEventListenerToNode(t,e,n,r){let s=function(t,e,n){return t=t._methodHost||t,function(e){t[n]?t[n](e,e.detail):console.warn("listener method `"+n+"` not defined")}}(r=r||t,0,n);return this._addEventListenerToNode(t,e,s),s}_addEventListenerToNode(t,e,n){t.addEventListener(e,n)}_removeEventListenerFromNode(t,e,n){t.removeEventListener(e,n)}}));let ce=0;const de=[],_e={COMPUTE:"__computeEffects",REFLECT:"__reflectEffects",NOTIFY:"__notifyEffects",PROPAGATE:"__propagateEffects",OBSERVE:"__observeEffects",READ_ONLY:"__readOnly"},ue="__computeInfo",pe=/[A-Z]/;function fe(t,e,n){let r=t[e];if(r){if(!t.hasOwnProperty(e)&&(r=t[e]=Object.create(t[e]),n))for(let t in r){let e=r[t],n=r[t]=Array(e.length);for(let t=0;t<e.length;t++)n[t]=e[t]}}else r=t[e]={};return r}function me(t,e,n,r,s,i){if(e){let o=!1;const a=ce++;for(let l in n){let h=e[s?Lt(l):l];if(h)for(let e,c=0,d=h.length;c<d&&(e=h[c]);c++)e.info&&e.info.lastRun===a||s&&!ge(l,e.trigger)||(e.info&&(e.info.lastRun=a),e.fn(t,l,n,r,e.info,s,i),o=!0)}return o}return!1}function ye(t,e,n,r,s,i,o,a){let l=!1,h=e[o?Lt(r):r];if(h)for(let e,c=0,d=h.length;c<d&&(e=h[c]);c++)e.info&&e.info.lastRun===n||o&&!ge(r,e.trigger)||(e.info&&(e.info.lastRun=n),e.fn(t,r,s,i,e.info,o,a),l=!0);return l}function ge(t,e){if(e){let n=e.name;return n==t||!(!e.structured||!Mt(n,t))||!(!e.wildcard||!Dt(n,t))}return!0}function be(t,e,n,r,s){let i="string"==typeof s.method?t[s.method]:s.method,o=s.property;i?i.call(t,t.__data[o],r[o]):s.dynamicFn||console.warn("observer method `"+s.method+"` not defined")}function Pe(t,e,n){let r=Lt(e);if(r!==e){return ve(t,Ut(r)+"-changed",n[e],e),!0}return!1}function ve(t,e,n,r){let s={value:n,queueProperty:!0};r&&(s.path=r),I(t).dispatchEvent(new CustomEvent(e,{detail:s}))}function Ce(t,e,n,r,s,i){let o=(i?Lt(e):e)!=e?e:null,a=o?jt(t,o):t.__data[e];o&&void 0===a&&(a=n[e]),ve(t,s.eventName,a,o)}function we(t,e,n,r,s){let i=t.__data[e];T&&(i=T(i,s.attrName,"attribute",t)),t._propertyToAttribute(e,s.attrName,i)}function Ee(t,e,n,r){let s=t[_e.COMPUTE];if(s)if(S){ce++;const i=function(t){let e=t.constructor.__orderedComputedDeps;if(!e){e=new Map;const n=t[_e.COMPUTE];let r,{counts:s,ready:i,total:o}=function(t){const e=t[ue],n={},r=t[_e.COMPUTE],s=[];let i=0;for(let t in e){const r=e[t];i+=n[t]=r.args.filter((t=>!t.literal)).length+(r.dynamicFn?1:0)}for(let t in r)e[t]||s.push(t);return{counts:n,ready:s,total:i}}(t);for(;r=i.shift();){e.set(r,e.size);const t=n[r];t&&t.forEach((t=>{const e=t.info.methodInfo;--o,0==--s[e]&&i.push(e)}))}if(0!==o){const e=t;console.warn(`Computed graph for ${e.localName} incomplete; circular?`)}t.constructor.__orderedComputedDeps=e}return e}(t),o=[];for(let t in e)Oe(t,s,o,i,r);let a;for(;a=o.shift();)Ae(t,"",e,n,a)&&Oe(a.methodInfo,s,o,i,r);Object.assign(n,t.__dataOld),Object.assign(e,t.__dataPending),t.__dataPending=null}else{let i=e;for(;me(t,s,i,n,r);)Object.assign(n,t.__dataOld),Object.assign(e,t.__dataPending),i=t.__dataPending,t.__dataPending=null}}const Te=(t,e,n)=>{let r=0,s=e.length-1,i=-1;for(;r<=s;){const o=r+s>>1,a=n.get(e[o].methodInfo)-n.get(t.methodInfo);if(a<0)r=o+1;else{if(!(a>0)){i=o;break}s=o-1}}i<0&&(i=s+1),e.splice(i,0,t)},Oe=(t,e,n,r,s)=>{const i=e[s?Lt(t):t];if(i)for(let e=0;e<i.length;e++){const o=i[e];o.info.lastRun===ce||s&&!ge(t,o.trigger)||(o.info.lastRun=ce,Te(o.info,n,r))}};function Ae(t,e,n,r,s){let i=Me(t,e,n,r,s);if(i===de)return!1;let o=s.methodInfo;return t.__dataHasAccessor&&t.__dataHasAccessor[o]?t._setPendingProperty(o,i,!0):(t[o]=i,!1)}function Ne(t,e,n,r,s,i,o){n.bindings=n.bindings||[];let a={kind:r,target:s,parts:i,literal:o,isCompound:1!==i.length};if(n.bindings.push(a),function(t){return Boolean(t.target)&&"attribute"!=t.kind&&"text"!=t.kind&&!t.isCompound&&"{"===t.parts[0].mode}(a)){let{event:t,negate:e}=a.parts[0];a.listenerEvent=t||Ut(s)+"-changed",a.listenerNegate=e}let l=e.nodeInfoList.length;for(let n=0;n<a.parts.length;n++){let r=a.parts[n];r.compoundIndex=n,xe(t,e,a,r,l)}}function xe(t,e,n,r,s){if(!r.literal)if("attribute"===n.kind&&"-"===n.target[0])console.warn("Cannot set attribute "+n.target+' because "-" is not a valid attribute starting character');else{let i=r.dependencies,o={index:s,binding:n,part:r,evaluator:t};for(let n=0;n<i.length;n++){let r=i[n];"string"==typeof r&&(r=Be(r),r.wildcard=!0),t._addTemplatePropertyEffect(e,r.rootProperty,{fn:Se,info:o,trigger:r})}}}function Se(t,e,n,r,s,i,o){let a=o[s.index],l=s.binding,h=s.part;if(i&&h.source&&e.length>h.source.length&&"property"==l.kind&&!l.isCompound&&a.__isPropertyEffectsClient&&a.__dataHasAccessor&&a.__dataHasAccessor[l.target]){let r=n[e];e=Rt(h.source,l.target,e),a._setPendingPropertyOrPath(e,r,!1,!0)&&t._enqueueClient(a)}else{let o=s.evaluator._evaluateBinding(t,h,e,n,r,i);o!==de&&function(t,e,n,r,s){s=function(t,e,n,r){if(n.isCompound){let s=t.__dataCompoundStorage[n.target];s[r.compoundIndex]=e,e=s.join("")}"attribute"!==n.kind&&("textContent"!==n.target&&("value"!==n.target||"input"!==t.localName&&"textarea"!==t.localName)||(e=null==e?"":e));return e}(e,s,n,r),T&&(s=T(s,n.target,n.kind,e));if("attribute"==n.kind)t._valueToNodeAttribute(e,s,n.target);else{let r=n.target;e.__isPropertyEffectsClient&&e.__dataHasAccessor&&e.__dataHasAccessor[r]?e[_e.READ_ONLY]&&e[_e.READ_ONLY][r]||e._setPendingProperty(r,s)&&t._enqueueClient(e):t._setUnmanagedPropertyToNode(e,r,s)}}(t,a,l,h,o)}}function Ie(t,e){if(e.isCompound){let n=t.__dataCompoundStorage||(t.__dataCompoundStorage={}),r=e.parts,s=new Array(r.length);for(let t=0;t<r.length;t++)s[t]=r[t].literal;let i=e.target;n[i]=s,e.literal&&"property"==e.kind&&("className"===i&&(t=I(t)),t[i]=e.literal)}}function ke(t,e,n){if(n.listenerEvent){let r=n.parts[0];t.addEventListener(n.listenerEvent,(function(t){!function(t,e,n,r,s){let i,o=t.detail,a=o&&o.path;a?(r=Rt(n,r,a),i=o&&o.value):i=t.currentTarget[n],i=s?!i:i,e[_e.READ_ONLY]&&e[_e.READ_ONLY][r]||!e._setPendingPropertyOrPath(r,i,!0,Boolean(a))||o&&o.queueProperty||e._invalidateProperties()}(t,e,n.target,r.source,r.negate)}))}}function Le(t,e,n,r,s,i){i=e.static||i&&("object"!=typeof i||i[e.methodName]);let o={methodName:e.methodName,args:e.args,methodInfo:s,dynamicFn:i};for(let s,i=0;i<e.args.length&&(s=e.args[i]);i++)s.literal||t._addPropertyEffect(s.rootProperty,n,{fn:r,info:o,trigger:s});return i&&t._addPropertyEffect(e.methodName,n,{fn:r,info:o}),o}function Me(t,e,n,r,s){let i=t._methodHost||t,o=i[s.methodName];if(o){let r=t._marshalArgs(s.args,e,n);return r===de?de:o.apply(i,r)}s.dynamicFn||console.warn("method `"+s.methodName+"` not defined")}const De=[],Re="(?:[a-zA-Z_$][\\w.:$\\-*]*)",Fe="(?:("+Re+"|(?:[-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?)|(?:(?:'(?:[^'\\\\]|\\\\.)*')|(?:\"(?:[^\"\\\\]|\\\\.)*\")))\\s*)",He=new RegExp("(\\[\\[|{{)\\s*(?:(!)\\s*)?"+("("+Re+"\\s*"+("(?:\\(\\s*(?:"+("(?:"+Fe+"(?:,\\s*"+Fe+")*)")+"?)\\)\\s*)")+"?)")+"(?:]]|}})","g");function ze(t){let e="";for(let n=0;n<t.length;n++){e+=t[n].literal||""}return e}function je(t){let e=t.match(/([^\s]+?)\(([\s\S]*)\)/);if(e){let t={methodName:e[1],static:!0,args:De};if(e[2].trim()){return function(t,e){return e.args=t.map((function(t){let n=Be(t);return n.literal||(e.static=!1),n}),this),e}(e[2].replace(/\\,/g,",").split(","),t)}return t}return null}function Be(t){let e=t.trim().replace(/,/g,",").replace(/\\(.)/g,"$1"),n={name:e,value:"",literal:!1},r=e[0];switch("-"===r&&(r=e[1]),r>="0"&&r<="9"&&(r="#"),r){case"'":case'"':n.value=e.slice(1,-1),n.literal=!0;break;case"#":n.value=Number(e),n.literal=!0}return n.literal||(n.rootProperty=Lt(e),n.structured=kt(e),n.structured&&(n.wildcard=".*"==e.slice(-2),n.wildcard&&(n.name=e.slice(0,-2)))),n}function Je(t,e,n){let r=jt(t,n);return void 0===r&&(r=e[n]),r}function qe(t,e,n,r){const s={indexSplices:r};t.notifyPath(n+".splices",s),t.notifyPath(n+".length",e.length)}function Ye(t,e,n,r,s,i){qe(t,e,n,[{index:r,addedCount:s,removed:i,object:e,type:"splice"}])}const $e=d((t=>{const e=he(Kt(t));return class extends e{constructor(){super(),this.__isPropertyEffectsClient=!0,this.__dataClientsReady,this.__dataPendingClients,this.__dataToNotify,this.__dataLinkedPaths,this.__dataHasPaths,this.__dataCompoundStorage,this.__dataHost,this.__dataTemp,this.__dataClientsInitialized,this.__data,this.__dataPending,this.__dataOld,this.__computeEffects,this.__computeInfo,this.__reflectEffects,this.__notifyEffects,this.__propagateEffects,this.__observeEffects,this.__readOnly,this.__templateInfo,this._overrideLegacyUndefined}get PROPERTY_EFFECT_TYPES(){return _e}_initializeProperties(){super._initializeProperties(),this._registerHost(),this.__dataClientsReady=!1,this.__dataPendingClients=null,this.__dataToNotify=null,this.__dataLinkedPaths=null,this.__dataHasPaths=!1,this.__dataCompoundStorage=this.__dataCompoundStorage||null,this.__dataHost=this.__dataHost||null,this.__dataTemp={},this.__dataClientsInitialized=!1}_registerHost(){if(Ue.length){let t=Ue[Ue.length-1];t._enqueueClient(this),this.__dataHost=t}}_initializeProtoProperties(t){this.__data=Object.create(t),this.__dataPending=Object.create(t),this.__dataOld={}}_initializeInstanceProperties(t){let e=this[_e.READ_ONLY];for(let n in t)e&&e[n]||(this.__dataPending=this.__dataPending||{},this.__dataOld=this.__dataOld||{},this.__data[n]=this.__dataPending[n]=t[n])}_addPropertyEffect(t,e,n){this._createPropertyAccessor(t,e==_e.READ_ONLY);let r=fe(this,e,!0)[t];r||(r=this[e][t]=[]),r.push(n)}_removePropertyEffect(t,e,n){let r=fe(this,e,!0)[t],s=r.indexOf(n);s>=0&&r.splice(s,1)}_hasPropertyEffect(t,e){let n=this[e];return Boolean(n&&n[t])}_hasReadOnlyEffect(t){return this._hasPropertyEffect(t,_e.READ_ONLY)}_hasNotifyEffect(t){return this._hasPropertyEffect(t,_e.NOTIFY)}_hasReflectEffect(t){return this._hasPropertyEffect(t,_e.REFLECT)}_hasComputedEffect(t){return this._hasPropertyEffect(t,_e.COMPUTE)}_setPendingPropertyOrPath(t,e,n,r){if(r||Lt(Array.isArray(t)?t[0]:t)!==t){if(!r){let n=jt(this,t);if(!(t=Bt(this,t,e))||!super._shouldPropertyChange(t,e,n))return!1}if(this.__dataHasPaths=!0,this._setPendingProperty(t,e,n))return function(t,e,n){let r=t.__dataLinkedPaths;if(r){let s;for(let i in r){let o=r[i];Dt(i,e)?(s=Rt(i,o,e),t._setPendingPropertyOrPath(s,n,!0,!0)):Dt(o,e)&&(s=Rt(o,i,e),t._setPendingPropertyOrPath(s,n,!0,!0))}}}(this,t,e),!0}else{if(this.__dataHasAccessor&&this.__dataHasAccessor[t])return this._setPendingProperty(t,e,n);this[t]=e}return!1}_setUnmanagedPropertyToNode(t,e,n){n===t[e]&&"object"!=typeof n||("className"===e&&(t=I(t)),t[e]=n)}_setPendingProperty(t,e,n){let r=this.__dataHasPaths&&kt(t),s=r?this.__dataTemp:this.__data;return!!this._shouldPropertyChange(t,e,s[t])&&(this.__dataPending||(this.__dataPending={},this.__dataOld={}),t in this.__dataOld||(this.__dataOld[t]=this.__data[t]),r?this.__dataTemp[t]=e:this.__data[t]=e,this.__dataPending[t]=e,(r||this[_e.NOTIFY]&&this[_e.NOTIFY][t])&&(this.__dataToNotify=this.__dataToNotify||{},this.__dataToNotify[t]=n),!0)}_setProperty(t,e){this._setPendingProperty(t,e,!0)&&this._invalidateProperties()}_invalidateProperties(){this.__dataReady&&this._flushProperties()}_enqueueClient(t){this.__dataPendingClients=this.__dataPendingClients||[],t!==this&&this.__dataPendingClients.push(t)}_flushClients(){this.__dataClientsReady?this.__enableOrFlushClients():(this.__dataClientsReady=!0,this._readyClients(),this.__dataReady=!0)}__enableOrFlushClients(){let t=this.__dataPendingClients;if(t){this.__dataPendingClients=null;for(let e=0;e<t.length;e++){let n=t[e];n.__dataEnabled?n.__dataPending&&n._flushProperties():n._enableProperties()}}}_readyClients(){this.__enableOrFlushClients()}setProperties(t,e){for(let n in t)!e&&this[_e.READ_ONLY]&&this[_e.READ_ONLY][n]||this._setPendingPropertyOrPath(n,t[n],!0);this._invalidateProperties()}ready(){this._flushProperties(),this.__dataClientsReady||this._flushClients(),this.__dataPending&&this._flushProperties()}_propertiesChanged(t,e,n){let r,s=this.__dataHasPaths;this.__dataHasPaths=!1,Ee(this,e,n,s),r=this.__dataToNotify,this.__dataToNotify=null,this._propagatePropertyChanges(e,n,s),this._flushClients(),me(this,this[_e.REFLECT],e,n,s),me(this,this[_e.OBSERVE],e,n,s),r&&function(t,e,n,r,s){let i,o,a=t[_e.NOTIFY],l=ce++;for(let o in e)e[o]&&(a&&ye(t,a,l,o,n,r,s)||s&&Pe(t,o,n))&&(i=!0);i&&(o=t.__dataHost)&&o._invalidateProperties&&o._invalidateProperties()}(this,r,e,n,s),1==this.__dataCounter&&(this.__dataTemp={})}_propagatePropertyChanges(t,e,n){this[_e.PROPAGATE]&&me(this,this[_e.PROPAGATE],t,e,n),this.__templateInfo&&this._runEffectsForTemplate(this.__templateInfo,t,e,n)}_runEffectsForTemplate(t,e,n,r){const s=(e,r)=>{me(this,t.propertyEffects,e,n,r,t.nodeList);for(let s=t.firstChild;s;s=s.nextSibling)this._runEffectsForTemplate(s,e,n,r)};t.runEffects?t.runEffects(s,e,r):s(e,r)}linkPaths(t,e){t=Ht(t),e=Ht(e),this.__dataLinkedPaths=this.__dataLinkedPaths||{},this.__dataLinkedPaths[t]=e}unlinkPaths(t){t=Ht(t),this.__dataLinkedPaths&&delete this.__dataLinkedPaths[t]}notifySplices(t,e){let n={path:""};qe(this,jt(this,t,n),n.path,e)}get(t,e){return jt(e||this,t)}set(t,e,n){n?Bt(n,t,e):this[_e.READ_ONLY]&&this[_e.READ_ONLY][t]||this._setPendingPropertyOrPath(t,e,!0)&&this._invalidateProperties()}push(t,...e){let n={path:""},r=jt(this,t,n),s=r.length,i=r.push(...e);return e.length&&Ye(this,r,n.path,s,e.length,[]),i}pop(t){let e={path:""},n=jt(this,t,e),r=Boolean(n.length),s=n.pop();return r&&Ye(this,n,e.path,n.length,0,[s]),s}splice(t,e,n,...r){let s,i={path:""},o=jt(this,t,i);return e<0?e=o.length-Math.floor(-e):e&&(e=Math.floor(e)),s=2===arguments.length?o.splice(e):o.splice(e,n,...r),(r.length||s.length)&&Ye(this,o,i.path,e,r.length,s),s}shift(t){let e={path:""},n=jt(this,t,e),r=Boolean(n.length),s=n.shift();return r&&Ye(this,n,e.path,0,0,[s]),s}unshift(t,...e){let n={path:""},r=jt(this,t,n),s=r.unshift(...e);return e.length&&Ye(this,r,n.path,0,e.length,[]),s}notifyPath(t,e){let n;if(1==arguments.length){let r={path:""};e=jt(this,t,r),n=r.path}else n=Array.isArray(t)?Ht(t):t;this._setPendingPropertyOrPath(n,e,!0,!0)&&this._invalidateProperties()}_createReadOnlyProperty(t,e){var n;this._addPropertyEffect(t,_e.READ_ONLY),e&&(this["_set"+(n=t,n[0].toUpperCase()+n.substring(1))]=function(e){this._setProperty(t,e)})}_createPropertyObserver(t,e,n){let r={property:t,method:e,dynamicFn:Boolean(n)};this._addPropertyEffect(t,_e.OBSERVE,{fn:be,info:r,trigger:{name:t}}),n&&this._addPropertyEffect(e,_e.OBSERVE,{fn:be,info:r,trigger:{name:e}})}_createMethodObserver(t,e){let n=je(t);if(!n)throw new Error("Malformed observer expression '"+t+"'");Le(this,n,_e.OBSERVE,Me,null,e)}_createNotifyingProperty(t){this._addPropertyEffect(t,_e.NOTIFY,{fn:Ce,info:{eventName:Ut(t)+"-changed",property:t}})}_createReflectedProperty(t){let e=this.constructor.attributeNameForProperty(t);"-"===e[0]?console.warn("Property "+t+" cannot be reflected to attribute "+e+' because "-" is not a valid starting attribute name. Use a lowercase first letter for the property instead.'):this._addPropertyEffect(t,_e.REFLECT,{fn:we,info:{attrName:e}})}_createComputedProperty(t,e,n){let r=je(e);if(!r)throw new Error("Malformed computed expression '"+e+"'");const s=Le(this,r,_e.COMPUTE,Ae,t,n);fe(this,ue)[t]=s}_marshalArgs(t,e,n){const r=this.__data,s=[];for(let i=0,o=t.length;i<o;i++){let{name:o,structured:a,wildcard:l,value:h,literal:c}=t[i];if(!c)if(l){const t=Dt(o,e),s=Je(r,n,t?e:o);h={path:t?e:o,value:s,base:t?jt(r,o):s}}else h=a?Je(r,n,o):r[o];s[i]=h}return s}static addPropertyEffect(t,e,n){this.prototype._addPropertyEffect(t,e,n)}static createPropertyObserver(t,e,n){this.prototype._createPropertyObserver(t,e,n)}static createMethodObserver(t,e){this.prototype._createMethodObserver(t,e)}static createNotifyingProperty(t){this.prototype._createNotifyingProperty(t)}static createReadOnlyProperty(t,e){this.prototype._createReadOnlyProperty(t,e)}static createReflectedProperty(t){this.prototype._createReflectedProperty(t)}static createComputedProperty(t,e,n){this.prototype._createComputedProperty(t,e,n)}static bindTemplate(t){return this.prototype._bindTemplate(t)}_bindTemplate(t,e){let n=this.constructor._parseTemplate(t),r=this.__preBoundTemplateInfo==n;if(!r)for(let t in n.propertyEffects)this._createPropertyAccessor(t);if(e)if(n=Object.create(n),n.wasPreBound=r,this.__templateInfo){const e=t._parentTemplateInfo||this.__templateInfo,r=e.lastChild;n.parent=e,e.lastChild=n,n.previousSibling=r,r?r.nextSibling=n:e.firstChild=n}else this.__templateInfo=n;else this.__preBoundTemplateInfo=n;return n}static _addTemplatePropertyEffect(t,e,n){(t.hostProps=t.hostProps||{})[e]=!0;let r=t.propertyEffects=t.propertyEffects||{};(r[e]=r[e]||[]).push(n)}_stampTemplate(t,e){e=e||this._bindTemplate(t,!0),Ue.push(this);let n=super._stampTemplate(t,e);if(Ue.pop(),e.nodeList=n.nodeList,!e.wasPreBound){let t=e.childNodes=[];for(let e=n.firstChild;e;e=e.nextSibling)t.push(e)}return n.templateInfo=e,function(t,e){let{nodeList:n,nodeInfoList:r}=e;if(r.length)for(let e=0;e<r.length;e++){let s=r[e],i=n[e],o=s.bindings;if(o)for(let e=0;e<o.length;e++){let n=o[e];Ie(i,n),ke(i,t,n)}i.__dataHost=t}}(this,e),this.__dataClientsReady&&(this._runEffectsForTemplate(e,this.__data,null,!1),this._flushClients()),n}_removeBoundDom(t){const e=t.templateInfo,{previousSibling:n,nextSibling:r,parent:s}=e;n?n.nextSibling=r:s&&(s.firstChild=r),r?r.previousSibling=n:s&&(s.lastChild=n),e.nextSibling=e.previousSibling=null;let i=e.childNodes;for(let t=0;t<i.length;t++){let e=i[t];I(I(e).parentNode).removeChild(e)}}static _parseTemplateNode(t,n,r){let s=e._parseTemplateNode.call(this,t,n,r);if(t.nodeType===Node.TEXT_NODE){let e=this._parseBindings(t.textContent,n);e&&(t.textContent=ze(e)||" ",Ne(this,n,r,"text","textContent",e),s=!0)}return s}static _parseTemplateNodeAttribute(t,n,r,s,i){let o=this._parseBindings(i,n);if(o){let e=s,i="property";pe.test(s)?i="attribute":"$"==s[s.length-1]&&(s=s.slice(0,-1),i="attribute");let a=ze(o);return a&&"attribute"==i&&("class"==s&&t.hasAttribute("class")&&(a+=" "+t.getAttribute(s)),t.setAttribute(s,a)),"attribute"==i&&"disable-upgrade$"==e&&t.setAttribute(s,""),"input"===t.localName&&"value"===e&&t.setAttribute(e,""),t.removeAttribute(e),"property"===i&&(s=$t(s)),Ne(this,n,r,i,s,o,a),!0}return e._parseTemplateNodeAttribute.call(this,t,n,r,s,i)}static _parseTemplateNestedTemplate(t,n,r){let s=e._parseTemplateNestedTemplate.call(this,t,n,r);const i=t.parentNode,o=r.templateInfo;i.localName,i.localName;let a=o.hostProps;{let t="{";for(let e in a){Ne(this,n,r,"property","_host_"+e,[{mode:t,source:e,dependencies:[e],hostProp:!0}])}}return s}static _parseBindings(t,e){let n,r=[],s=0;for(;null!==(n=He.exec(t));){n.index>s&&r.push({literal:t.slice(s,n.index)});let i=n[1][0],o=Boolean(n[2]),a=n[3].trim(),l=!1,h="",c=-1;"{"==i&&(c=a.indexOf("::"))>0&&(h=a.substring(c+2),a=a.substring(0,c),l=!0);let d=je(a),_=[];if(d){let{args:t,methodName:n}=d;for(let e=0;e<t.length;e++){let n=t[e];n.literal||_.push(n)}let r=e.dynamicFns;(r&&r[n]||d.static)&&(_.push(n),d.dynamicFn=!0)}else _.push(a);r.push({source:a,mode:i,negate:o,customEvent:l,signature:d,dependencies:_,event:h}),s=He.lastIndex}if(s&&s<t.length){let e=t.substring(s);e&&r.push({literal:e})}return r.length?r:null}static _evaluateBinding(t,e,n,r,s,i){let o;return o=e.signature?Me(t,n,r,0,e.signature):n!=e.source?jt(t,e.source):i&&kt(n)?jt(t,n):t.__data[n],e.negate&&(o=!o),o}}})),Ue=[];const Ve=d((t=>{const e=Xt(t);function n(t){const e=Object.getPrototypeOf(t);return e.prototype instanceof s?e:null}function r(t){if(!t.hasOwnProperty(JSCompiler_renameProperty("__ownProperties",t))){let e=null;if(t.hasOwnProperty(JSCompiler_renameProperty("properties",t))){const n=t.properties;n&&(e=function(t){const e={};for(let n in t){const r=t[n];e[n]="function"==typeof r?{type:r}:r}return e}(n))}t.__ownProperties=e}return t.__ownProperties}class s extends e{static get observedAttributes(){if(!this.hasOwnProperty(JSCompiler_renameProperty("__observedAttributes",this))){this.prototype;const t=this._properties;this.__observedAttributes=t?Object.keys(t).map((t=>this.prototype._addPropertyToAttributeMap(t))):[]}return this.__observedAttributes}static finalize(){if(!this.hasOwnProperty(JSCompiler_renameProperty("__finalized",this))){const t=n(this);t&&t.finalize(),this.__finalized=!0,this._finalizeClass()}}static _finalizeClass(){const t=r(this);t&&this.createProperties(t)}static get _properties(){if(!this.hasOwnProperty(JSCompiler_renameProperty("__properties",this))){const t=n(this);this.__properties=Object.assign({},t&&t._properties,r(this))}return this.__properties}static typeForProperty(t){const e=this._properties[t];return e&&e.type}_initializeProperties(){this.constructor.finalize(),super._initializeProperties()}connectedCallback(){super.connectedCallback&&super.connectedCallback(),this._enableProperties()}disconnectedCallback(){super.disconnectedCallback&&super.disconnectedCallback()}}return s})),Xe=d((t=>{const e=Ve($e(t));function n(t,e,n,r){n.computed&&(n.readOnly=!0),n.computed&&(t._hasReadOnlyEffect(e)?console.warn(`Cannot redefine computed property '${e}'.`):t._createComputedProperty(e,n.computed,r)),n.readOnly&&!t._hasReadOnlyEffect(e)?t._createReadOnlyProperty(e,!n.computed):!1===n.readOnly&&t._hasReadOnlyEffect(e)&&console.warn(`Cannot make readOnly property '${e}' non-readOnly.`),n.reflectToAttribute&&!t._hasReflectEffect(e)?t._createReflectedProperty(e):!1===n.reflectToAttribute&&t._hasReflectEffect(e)&&console.warn(`Cannot make reflected property '${e}' non-reflected.`),n.notify&&!t._hasNotifyEffect(e)?t._createNotifyingProperty(e):!1===n.notify&&t._hasNotifyEffect(e)&&console.warn(`Cannot make notify property '${e}' non-notify.`),n.observer&&t._createPropertyObserver(e,n.observer,r[n.observer]),t._addPropertyToAttributeMap(e)}function r(t,e,n,r){{const s=e.content.querySelectorAll("style"),i=xt(e),o=function(t){let e=Tt(t);return e?St(e):[]}(n),a=e.content.firstElementChild;for(let n=0;n<o.length;n++){let s=o[n];s.textContent=t._processStyleText(s.textContent,r),e.content.insertBefore(s,a)}let l=0;for(let e=0;e<i.length;e++){let n=i[e],o=s[l];o!==n?(n=n.cloneNode(!0),o.parentNode.insertBefore(n,o)):l++,n.textContent=t._processStyleText(n.textContent,r)}}}return class extends e{static get polymerElementVersion(){return"3.5.0"}static _finalizeClass(){e._finalizeClass.call(this);const t=((n=this).hasOwnProperty(JSCompiler_renameProperty("__ownObservers",n))||(n.__ownObservers=n.hasOwnProperty(JSCompiler_renameProperty("observers",n))?n.observers:null),n.__ownObservers);var n;t&&this.createObservers(t,this._properties),this._prepareTemplate()}static _prepareTemplate(){let t=this.template;t&&("string"==typeof t?(console.error("template getter must return HTMLTemplateElement"),t=null):x||(t=t.cloneNode(!0))),this.prototype._template=t}static createProperties(t){for(let e in t)n(this.prototype,e,t[e],t)}static createObservers(t,e){const n=this.prototype;for(let r=0;r<t.length;r++)n._createMethodObserver(t[r],e)}static get template(){if(!this.hasOwnProperty(JSCompiler_renameProperty("_template",this))){let t=this.prototype.hasOwnProperty(JSCompiler_renameProperty("_template",this.prototype))?this.prototype._template:void 0;"function"==typeof t&&(t=t()),this._template=void 0!==t?t:this.hasOwnProperty(JSCompiler_renameProperty("is",this))&&function(t){let e=null;if(t&&(!A||N)&&(e=wt.import(t,"template"),A&&!e))throw new Error(`strictTemplatePolicy: expecting dom-module or null template for ${t}`);return e}(this.is)||Object.getPrototypeOf(this.prototype).constructor.template}return this._template}static set template(t){this._template=t}static get importPath(){if(!this.hasOwnProperty(JSCompiler_renameProperty("_importPath",this))){const t=this.importMeta;if(t)this._importPath=C(t.url);else{const t=wt.import(this.is);this._importPath=t&&t.assetpath||Object.getPrototypeOf(this.prototype).constructor.importPath}}return this._importPath}constructor(){super(),this._template,this._importPath,this.rootPath,this.importPath,this.root,this.$}_initializeProperties(){this.constructor.finalize(),this.constructor._finalizeTemplate(this.localName),super._initializeProperties(),this.rootPath=E,this.importPath=this.constructor.importPath;let t=function(t){if(!t.hasOwnProperty(JSCompiler_renameProperty("__propertyDefaults",t))){t.__propertyDefaults=null;let e=t._properties;for(let n in e){let r=e[n];"value"in r&&(t.__propertyDefaults=t.__propertyDefaults||{},t.__propertyDefaults[n]=r)}}return t.__propertyDefaults}(this.constructor);if(t)for(let e in t){let n=t[e];if(this._canApplyPropertyDefault(e)){let t="function"==typeof n.value?n.value.call(this):n.value;this._hasAccessor(e)?this._setPendingProperty(e,t,!0):this[e]=t}}}_canApplyPropertyDefault(t){return!this.hasOwnProperty(t)}static _processStyleText(t,e){return v(t,e)}static _finalizeTemplate(t){const e=this.prototype._template;if(e&&!e.__polymerFinalized){e.__polymerFinalized=!0;const n=this.importPath;r(this,e,t,n?P(n):""),this.prototype._bindTemplate(e)}}connectedCallback(){super.connectedCallback()}ready(){this._template&&(this.root=this._stampTemplate(this._template),this.$=this.root.$),super.ready()}_readyClients(){this._template&&(this.root=this._attachDom(this.root)),super._readyClients()}_attachDom(t){const e=I(this);if(e.attachShadow)return t?(e.shadowRoot||(e.attachShadow({mode:"open",shadyUpgradeFragment:t}),this.constructor._styleSheet&&(e.shadowRoot.adoptedStyleSheets=[this.constructor._styleSheet])),e.shadowRoot.appendChild(t),e.shadowRoot):null;throw new Error("ShadowDOM not available. PolymerElement can create dom as children instead of in ShadowDOM by setting `this.root = this;` before `ready`.")}updateStyles(t){for(const[e,n]of Object.entries(t))this.style.setProperty(e,n)}resolveUrl(t,e){return!e&&this.importPath&&(e=P(this.importPath)),P(t,e)}static _parseTemplateContent(t,n,r){return n.dynamicFns=n.dynamicFns||this._properties,e._parseTemplateContent.call(this,t,n,r)}static _addTemplatePropertyEffect(t,n,r){return e._addTemplatePropertyEffect.call(this,t,n,r)}}})),Ge=window.trustedTypes&&trustedTypes.createPolicy("polymer-html-literal",{createHTML:t=>t});class We{constructor(t,e){Qe(t,e);const n=e.reduce(((e,n,r)=>e+Ze(n)+t[r+1]),t[0]);this.value=n.toString()}toString(){return this.value}}function Ze(t){if(t instanceof We)return t.value;throw new Error(`non-literal value passed to Polymer's htmlLiteral function: ${t}`)}const Ke=function(t,...e){Qe(t,e);const n=document.createElement("template");let r=e.reduce(((e,n,r)=>e+function(t){if(t instanceof HTMLTemplateElement)return t.innerHTML;if(t instanceof We)return Ze(t);throw new Error(`non-template value passed to Polymer's html function: ${t}`)}(n)+t[r+1]),t[0]);return Ge&&(r=Ge.createHTML(r)),n.innerHTML=r,n},Qe=(t,e)=>{if(!Array.isArray(t)||!Array.isArray(t.raw)||e.length!==t.length-1)throw new TypeError("Invalid call to the html template tag")},tn=Xe(HTMLElement),en=function(){let t,e;do{t=!1,e=f()}while(e)};function nn(t,e,n,r,s){let i;s&&(i="object"==typeof n&&null!==n,i&&(r=t.__dataTemp[e]));let o=r!==n&&(r==r||n==n);return i&&o&&(t.__dataTemp[e]=n),o}const rn=d((t=>class extends t{_shouldPropertyChange(t,e,n){return nn(this,t,e,n,!0)}})),sn=d((t=>class extends t{static get properties(){return{mutableData:Boolean}}_shouldPropertyChange(t,e,n){return nn(this,t,e,n,this.mutableData)}}));rn._mutablePropertyChange=nn;let on=null;function an(){return on}an.prototype=Object.create(HTMLTemplateElement.prototype,{constructor:{value:an,writable:!0}});const ln=$e(an),hn=rn(ln);const cn=$e(class{});class dn extends cn{constructor(t){super(),this._configureProperties(t),this.root=this._stampTemplate(this.__dataHost);let e=[];this.children=e;for(let t=this.root.firstChild;t;t=t.nextSibling)e.push(t),t.__templatizeInstance=this;this.__templatizeOwner&&this.__templatizeOwner.__hideTemplateChildren__&&this._showHideChildren(!0);let n=this.__templatizeOptions;(t&&n.instanceProps||!n.instanceProps)&&this._enableProperties()}_configureProperties(t){if(this.__templatizeOptions.forwardHostProp)for(let t in this.__hostProps)this._setPendingProperty(t,this.__dataHost["_host_"+t]);for(let e in t)this._setPendingProperty(e,t[e])}forwardHostProp(t,e){this._setPendingPropertyOrPath(t,e,!1,!0)&&this.__dataHost._enqueueClient(this)}_addEventListenerToNode(t,e,n){if(this._methodHost&&this.__templatizeOptions.parentModel)this._methodHost._addEventListenerToNode(t,e,(t=>{t.model=this,n(t)}));else{let r=this.__dataHost.__dataHost;r&&r._addEventListenerToNode(t,e,n)}}_showHideChildren(t){!function(t,e){for(let n=0;n<e.length;n++){let r=e[n];if(Boolean(t)!=Boolean(r.__hideTemplateChildren__))if(r.nodeType===Node.TEXT_NODE)t?(r.__polymerTextContent__=r.textContent,r.textContent=""):r.textContent=r.__polymerTextContent__;else if("slot"===r.localName)if(t)r.__polymerReplaced__=document.createComment("hidden-slot"),I(I(r).parentNode).replaceChild(r.__polymerReplaced__,r);else{const t=r.__polymerReplaced__;t&&I(I(t).parentNode).replaceChild(r,t)}else r.style&&(t?(r.__polymerDisplay__=r.style.display,r.style.display="none"):r.style.display=r.__polymerDisplay__);r.__hideTemplateChildren__=t,r._showHideChildren&&r._showHideChildren(t)}}(t,this.children)}_setUnmanagedPropertyToNode(t,e,n){t.__hideTemplateChildren__&&t.nodeType==Node.TEXT_NODE&&"textContent"==e?t.__polymerTextContent__=n:super._setUnmanagedPropertyToNode(t,e,n)}get parentModel(){let t=this.__parentModel;if(!t){let e;t=this;do{t=t.__dataHost.__dataHost}while((e=t.__templatizeOptions)&&!e.parentModel);this.__parentModel=t}return t}dispatchEvent(t){return!0}}dn.prototype.__dataHost,dn.prototype.__templatizeOptions,dn.prototype._methodHost,dn.prototype.__templatizeOwner,dn.prototype.__hostProps;const _n=rn(dn);function un(t){let e=t.__dataHost;return e&&e._methodHost||e}function pn(t,e,n){let r=n.mutableData?_n:dn;gn.mixin&&(r=gn.mixin(r));let s=class extends r{};return s.prototype.__templatizeOptions=n,s.prototype._bindTemplate(t),function(t,e,n,r){let s=n.hostProps||{};for(let e in r.instanceProps){delete s[e];let n=r.notifyInstanceProp;n&&t.prototype._addPropertyEffect(e,t.prototype.PROPERTY_EFFECT_TYPES.NOTIFY,{fn:yn(e,n)})}if(r.forwardHostProp&&e.__dataHost)for(let e in s)n.hasHostProps||(n.hasHostProps=!0),t.prototype._addPropertyEffect(e,t.prototype.PROPERTY_EFFECT_TYPES.NOTIFY,{fn:function(t,e,n){t.__dataHost._setPendingPropertyOrPath("_host_"+e,n[e],!0,!0)}})}(s,t,e,n),s}function fn(t,e,n,r){let s=n.forwardHostProp;if(s&&e.hasHostProps){const r="template"==t.localName;let a=e.templatizeTemplateClass;if(!a){if(r){let t=n.mutableData?hn:ln;class r extends t{}a=e.templatizeTemplateClass=r}else{const n=t.constructor;class r extends n{}a=e.templatizeTemplateClass=r}let i=e.hostProps;for(let t in i)a.prototype._addPropertyEffect("_host_"+t,a.prototype.PROPERTY_EFFECT_TYPES.PROPAGATE,{fn:mn(t,s)}),a.prototype._createNotifyingProperty("_host_"+t)}if(t.__dataProto&&Object.assign(t.__data,t.__dataProto),r)o=a,on=i=t,Object.setPrototypeOf(i,o.prototype),new o,on=null,t.__dataTemp={},t.__dataPending=null,t.__dataOld=null,t._enableProperties();else{Object.setPrototypeOf(t,a.prototype);const n=e.hostProps;for(let e in n)if(e="_host_"+e,e in t){const n=t[e];delete t[e],t.__data[e]=n}}}var i,o}function mn(t,e){return function(t,n,r){e.call(t.__templatizeOwner,n.substring("_host_".length),r[n])}}function yn(t,e){return function(t,n,r){e.call(t.__templatizeOwner,t,n,r[n])}}function gn(t,e,n){if(A&&!un(t))throw new Error("strictTemplatePolicy: template owner not trusted");if(n=n||{},t.__templatizeOwner)throw new Error("A <template> can only be templatized once");t.__templatizeOwner=e;let r=(e?e.constructor:dn)._parseTemplate(t),s=r.templatizeInstanceClass;s||(s=pn(t,r,n),r.templatizeInstanceClass=s);const i=un(t);fn(t,r,n);let o=class extends s{};return o.prototype._methodHost=i,o.prototype.__dataHost=t,o.prototype.__templatizeOwner=e,o.prototype.__hostProps=r.hostProps,o}function bn(t,e){let n;for(;e;)if(n=e.__dataHost?e:e.__templatizeInstance){if(n.__dataHost==t)return n;e=n.__dataHost}else e=I(e).parentNode;return null}class Pn extends tn{static get is(){return"dom-if"}static get template(){return null}static get properties(){return{if:{type:Boolean,observer:"__debounceRender"},restamp:{type:Boolean,observer:"__debounceRender"},notifyDomChange:{type:Boolean}}}constructor(){super(),this.__renderDebouncer=null,this._lastIf=!1,this.__hideTemplateChildren__=!1,this.__template,this._templateInfo}__debounceRender(){this.__renderDebouncer=_.debounce(this.__renderDebouncer,h,(()=>this.__render())),p(this.__renderDebouncer)}disconnectedCallback(){super.disconnectedCallback();const t=I(this).parentNode;t&&(t.nodeType!=Node.DOCUMENT_FRAGMENT_NODE||I(t).host)||this.__teardownInstance()}connectedCallback(){super.connectedCallback(),this.style.display="none",this.if&&this.__debounceRender()}__ensureTemplate(){if(!this.__template){const t=this;let e=t._templateInfo?t:I(t).querySelector("template");if(!e){let t=new MutationObserver((()=>{if(!I(this).querySelector("template"))throw new Error("dom-if requires a <template> child");t.disconnect(),this.__render()}));return t.observe(this,{childList:!0}),!1}this.__template=e}return!0}__ensureInstance(){let t=I(this).parentNode;if(this.__hasInstance()){let e=this.__getInstanceNodes();if(e&&e.length){if(I(this).previousSibling!==e[e.length-1])for(let n,r=0;r<e.length&&(n=e[r]);r++)I(t).insertBefore(n,this)}}else{if(!t)return!1;if(!this.__ensureTemplate())return!1;this.__createAndInsertInstance(t)}return!0}render(){en()}__render(){if(this.if){if(!this.__ensureInstance())return}else this.restamp&&this.__teardownInstance();this._showHideChildren(),this.if!=this._lastIf&&(this.dispatchEvent(new CustomEvent("dom-change",{bubbles:!0,composed:!0})),this._lastIf=this.if)}__hasInstance(){}__getInstanceNodes(){}__createAndInsertInstance(t){}__teardownInstance(){}_showHideChildren(){}}const vn=class extends Pn{constructor(){super(),this.__ctor=null,this.__instance=null,this.__invalidProps=null}__hasInstance(){return Boolean(this.__instance)}__getInstanceNodes(){return this.__instance.children}__createAndInsertInstance(t){this.__ctor||(this.__ctor=gn(this.__template,this,{mutableData:!0,forwardHostProp:function(t,e){this.__instance&&(this.if?this.__instance.forwardHostProp(t,e):(this.__invalidProps=this.__invalidProps||Object.create(null),this.__invalidProps[Lt(t)]=!0))}})),this.__instance=new this.__ctor,I(t).insertBefore(this.__instance.root,this)}__teardownInstance(){if(this.__instance){let t=this.__instance.children;if(t&&t.length){let e=I(t[0]).parentNode;if(e){e=I(e);for(let n,r=0;r<t.length&&(n=t[r]);r++)e.removeChild(n)}}this.__invalidProps=null,this.__instance=null}}__syncHostProperties(){let t=this.__invalidProps;if(t){this.__invalidProps=null;for(let e in t)this.__instance._setPendingProperty(e,this.__dataHost[e]);this.__instance._flushProperties()}}_showHideChildren(){const t=this.__hideTemplateChildren__||!this.if;this.__instance&&Boolean(this.__instance.__hidden)!==t&&(this.__instance.__hidden=t,this.__instance._showHideChildren(t)),t||this.__syncHostProperties()}};customElements.define(vn.is,vn);const Cn=sn(tn);class wn extends Cn{static get is(){return"dom-repeat"}static get template(){return null}static get properties(){return{items:{type:Array},as:{type:String,value:"item"},indexAs:{type:String,value:"index"},itemsIndexAs:{type:String,value:"itemsIndex"},sort:{type:Function,observer:"__sortChanged"},filter:{type:Function,observer:"__filterChanged"},observe:{type:String,observer:"__observeChanged"},delay:Number,renderedItemCount:{type:Number,notify:!0,readOnly:!0},initialCount:{type:Number},targetFramerate:{type:Number,value:20},_targetFrameTime:{type:Number,computed:"__computeFrameTime(targetFramerate)"},notifyDomChange:{type:Boolean},reuseChunkedInstances:{type:Boolean}}}static get observers(){return["__itemsChanged(items.*)"]}constructor(){super(),this.__instances=[],this.__renderDebouncer=null,this.__itemsIdxToInstIdx={},this.__chunkCount=null,this.__renderStartTime=null,this.__itemsArrayChanged=!1,this.__shouldMeasureChunk=!1,this.__shouldContinueChunking=!1,this.__chunkingId=0,this.__sortFn=null,this.__filterFn=null,this.__observePaths=null,this.__ctor=null,this.__isDetached=!0,this.template=null,this._templateInfo}disconnectedCallback(){super.disconnectedCallback(),this.__isDetached=!0;for(let t=0;t<this.__instances.length;t++)this.__detachInstance(t);this.__chunkingId&&cancelAnimationFrame(this.__chunkingId)}connectedCallback(){if(super.connectedCallback(),this.style.display="none",this.__isDetached){this.__isDetached=!1;let t=I(I(this).parentNode);for(let e=0;e<this.__instances.length;e++)this.__attachInstance(e,t);this.__chunkingId&&this.__render()}}__ensureTemplatized(){if(!this.__ctor){const t=this;let e=this.template=t._templateInfo?t:this.querySelector("template");if(!e){let t=new MutationObserver((()=>{if(!this.querySelector("template"))throw new Error("dom-repeat requires a <template> child");t.disconnect(),this.__render()}));return t.observe(this,{childList:!0}),!1}let n={};n[this.as]=!0,n[this.indexAs]=!0,n[this.itemsIndexAs]=!0,this.__ctor=gn(e,this,{mutableData:this.mutableData,parentModel:!0,instanceProps:n,forwardHostProp:function(t,e){let n=this.__instances;for(let r,s=0;s<n.length&&(r=n[s]);s++)r.forwardHostProp(t,e)},notifyInstanceProp:function(t,e,n){if(Ft(this.as,e)){let r=t[this.itemsIndexAs];e==this.as&&(this.items[r]=n);let s=Rt(this.as,`${JSCompiler_renameProperty("items",this)}.${r}`,e);this.notifyPath(s,n)}}})}return!0}__getMethodHost(){return this.__dataHost._methodHost||this.__dataHost}__functionFromPropertyValue(t){if("string"==typeof t){let e=t,n=this.__getMethodHost();return function(){return n[e].apply(n,arguments)}}return t}__sortChanged(t){this.__sortFn=this.__functionFromPropertyValue(t),this.items&&this.__debounceRender(this.__render)}__filterChanged(t){this.__filterFn=this.__functionFromPropertyValue(t),this.items&&this.__debounceRender(this.__render)}__computeFrameTime(t){return Math.ceil(1e3/t)}__observeChanged(){this.__observePaths=this.observe&&this.observe.replace(".*",".").split(" ")}__handleObservedPaths(t){if(this.__sortFn||this.__filterFn)if(t){if(this.__observePaths){let e=this.__observePaths;for(let n=0;n<e.length;n++)0===t.indexOf(e[n])&&this.__debounceRender(this.__render,this.delay)}}else this.__debounceRender(this.__render,this.delay)}__itemsChanged(t){this.items&&!Array.isArray(this.items)&&console.warn("dom-repeat expected array for `items`, found",this.items),this.__handleItemPath(t.path,t.value)||("items"===t.path&&(this.__itemsArrayChanged=!0),this.__debounceRender(this.__render))}__debounceRender(t,e=0){this.__renderDebouncer=_.debounce(this.__renderDebouncer,e>0?o.after(e):h,t.bind(this)),p(this.__renderDebouncer)}render(){this.__debounceRender(this.__render),en()}__render(){if(!this.__ensureTemplatized())return;let t=this.items||[];const e=this.__sortAndFilterItems(t),n=this.__calculateLimit(e.length);this.__updateInstances(t,n,e),this.initialCount&&(this.__shouldMeasureChunk||this.__shouldContinueChunking)&&(cancelAnimationFrame(this.__chunkingId),this.__chunkingId=requestAnimationFrame((()=>{this.__chunkingId=null,this.__continueChunking()}))),this._setRenderedItemCount(this.__instances.length),this.dispatchEvent(new CustomEvent("dom-change",{bubbles:!0,composed:!0}))}__sortAndFilterItems(t){let e=new Array(t.length);for(let n=0;n<t.length;n++)e[n]=n;return this.__filterFn&&(e=e.filter(((e,n,r)=>this.__filterFn(t[e],n,r)))),this.__sortFn&&e.sort(((e,n)=>this.__sortFn(t[e],t[n]))),e}__calculateLimit(t){let e=t;const n=this.__instances.length;if(this.initialCount){let r;!this.__chunkCount||this.__itemsArrayChanged&&!this.reuseChunkedInstances?(e=Math.min(t,this.initialCount),r=Math.max(e-n,0),this.__chunkCount=r||1):(r=Math.min(Math.max(t-n,0),this.__chunkCount),e=Math.min(n+r,t)),this.__shouldMeasureChunk=r===this.__chunkCount,this.__shouldContinueChunking=e<t,this.__renderStartTime=performance.now()}return this.__itemsArrayChanged=!1,e}__continueChunking(){if(this.__shouldMeasureChunk){const t=performance.now()-this.__renderStartTime,e=this._targetFrameTime/t;this.__chunkCount=Math.round(this.__chunkCount*e)||1}this.__shouldContinueChunking&&this.__debounceRender(this.__render)}__updateInstances(t,e,n){const r=this.__itemsIdxToInstIdx={};let s;for(s=0;s<e;s++){let e=this.__instances[s],i=n[s],o=t[i];r[i]=s,e?(e._setPendingProperty(this.as,o),e._setPendingProperty(this.indexAs,s),e._setPendingProperty(this.itemsIndexAs,i),e._flushProperties()):this.__insertInstance(o,s,i)}for(let t=this.__instances.length-1;t>=s;t--)this.__detachAndRemoveInstance(t)}__detachInstance(t){let e=this.__instances[t];const n=I(e.root);for(let t=0;t<e.children.length;t++){let r=e.children[t];n.appendChild(r)}return e}__attachInstance(t,e){let n=this.__instances[t];e.insertBefore(n.root,this)}__detachAndRemoveInstance(t){this.__detachInstance(t),this.__instances.splice(t,1)}__stampInstance(t,e,n){let r={};return r[this.as]=t,r[this.indexAs]=e,r[this.itemsIndexAs]=n,new this.__ctor(r)}__insertInstance(t,e,n){const r=this.__stampInstance(t,e,n);let s=this.__instances[e+1],i=s?s.children[0]:this;return I(I(this).parentNode).insertBefore(r.root,i),this.__instances[e]=r,r}_showHideChildren(t){for(let e=0;e<this.__instances.length;e++)this.__instances[e]._showHideChildren(t)}__handleItemPath(t,e){let n=t.slice(6),r=n.indexOf("."),s=r<0?n:n.substring(0,r);if(s==parseInt(s,10)){let t=r<0?"":n.substring(r+1);this.__handleObservedPaths(t);let i=this.__itemsIdxToInstIdx[s],o=this.__instances[i];if(o){let n=this.as+(t?"."+t:"");o._setPendingPropertyOrPath(n,e,!1,!0),o._flushProperties()}return!0}}itemForElement(t){let e=this.modelForElement(t);return e&&e[this.as]}indexForElement(t){let e=this.modelForElement(t);return e&&e[this.indexAs]}modelForElement(t){return bn(this.template,t)}}customElements.define(wn.is,wn);const En=d((t=>class extends t{_addEventListenerToNode(t,e,n){it(t,e,n)||super._addEventListenerToNode(t,e,n)}_removeEventListenerFromNode(t,e,n){ot(t,e,n)||super._removeEventListenerFromNode(t,e,n)}}));let Tn=!1,On=[],An=[];function Nn(){Tn=!0,requestAnimationFrame((function(){Tn=!1,function(t){for(;t.length;)xn(t.shift())}(On),setTimeout((function(){!function(t){for(let e=0,n=t.length;e<n;e++)xn(t.shift())}(An)}))}))}function xn(t){const e=t[0],n=t[1],r=t[2];try{n.apply(e,r)}catch(t){setTimeout((()=>{throw t}))}}function Sn(t,e,n){Tn||Nn(),On.push([t,e,n])}function In(t,e,n){Tn||Nn(),An.push([t,e,n])}function kn(){document.body.removeAttribute("unresolved")}function Ln(t,e,n){return{index:t,removed:e,addedCount:n}}"interactive"===document.readyState||"complete"===document.readyState?kn():window.addEventListener("DOMContentLoaded",kn);function Mn(t,e,n,r,s,i){let o,a=0,l=0,h=Math.min(n-e,i-s);if(0==e&&0==s&&(a=function(t,e,n){for(let r=0;r<n;r++)if(!Rn(t[r],e[r]))return r;return n}(t,r,h)),n==t.length&&i==r.length&&(l=function(t,e,n){let r=t.length,s=e.length,i=0;for(;i<n&&Rn(t[--r],e[--s]);)i++;return i}(t,r,h-a)),s+=a,i-=l,(n-=l)-(e+=a)==0&&i-s==0)return[];if(e==n){for(o=Ln(e,[],0);s<i;)o.removed.push(r[s++]);return[o]}if(s==i)return[Ln(e,[],n-e)];let c=function(t){let e=t.length-1,n=t[0].length-1,r=t[e][n],s=[];for(;e>0||n>0;){if(0==e){s.push(2),n--;continue}if(0==n){s.push(3),e--;continue}let i,o=t[e-1][n-1],a=t[e-1][n],l=t[e][n-1];i=a<l?a<o?a:o:l<o?l:o,i==o?(o==r?s.push(0):(s.push(1),r=o),e--,n--):i==a?(s.push(3),e--,r=a):(s.push(2),n--,r=l)}return s.reverse(),s}(function(t,e,n,r,s,i){let o=i-s+1,a=n-e+1,l=new Array(o);for(let t=0;t<o;t++)l[t]=new Array(a),l[t][0]=t;for(let t=0;t<a;t++)l[0][t]=t;for(let n=1;n<o;n++)for(let i=1;i<a;i++)if(Rn(t[e+i-1],r[s+n-1]))l[n][i]=l[n-1][i-1];else{let t=l[n-1][i]+1,e=l[n][i-1]+1;l[n][i]=t<e?t:e}return l}(t,e,n,r,s,i));o=void 0;let d=[],_=e,u=s;for(let t=0;t<c.length;t++)switch(c[t]){case 0:o&&(d.push(o),o=void 0),_++,u++;break;case 1:o||(o=Ln(_,[],0)),o.addedCount++,_++,o.removed.push(r[u]),u++;break;case 2:o||(o=Ln(_,[],0)),o.addedCount++,_++;break;case 3:o||(o=Ln(_,[],0)),o.removed.push(r[u]),u++}return o&&d.push(o),d}function Dn(t,e){return Mn(t,0,t.length,e,0,e.length)}function Rn(t,e){return t===e}function Fn(t){return"slot"===t.localName}let Hn=class{static getFlattenedNodes(t){const e=I(t);return Fn(t)?e.assignedNodes({flatten:!0}):Array.from(e.childNodes).map((t=>Fn(t)?I(t).assignedNodes({flatten:!0}):[t])).reduce(((t,e)=>t.concat(e)),[])}constructor(t,e){this._shadyChildrenObserver=null,this._nativeChildrenObserver=null,this._connected=!1,this._target=t,this.callback=e,this._effectiveNodes=[],this._observer=null,this._scheduled=!1,this._boundSchedule=()=>{this._schedule()},this.connect(),this._schedule()}connect(){Fn(this._target)?this._listenSlots([this._target]):I(this._target).children&&(this._listenSlots(I(this._target).children),this._nativeChildrenObserver=new MutationObserver((t=>{this._processMutations(t)})),this._nativeChildrenObserver.observe(this._target,{childList:!0})),this._connected=!0}disconnect(){Fn(this._target)?this._unlistenSlots([this._target]):I(this._target).children&&(this._unlistenSlots(I(this._target).children),this._nativeChildrenObserver&&(this._nativeChildrenObserver.disconnect(),this._nativeChildrenObserver=null)),this._connected=!1}_schedule(){this._scheduled||(this._scheduled=!0,h.run((()=>this.flush())))}_processMutations(t){this._processSlotMutations(t),this.flush()}_processSlotMutations(t){if(t)for(let e=0;e<t.length;e++){let n=t[e];n.addedNodes&&this._listenSlots(n.addedNodes),n.removedNodes&&this._unlistenSlots(n.removedNodes)}}flush(){if(!this._connected)return!1;this._nativeChildrenObserver?this._processSlotMutations(this._nativeChildrenObserver.takeRecords()):this._shadyChildrenObserver&&this._processSlotMutations(this._shadyChildrenObserver.takeRecords()),this._scheduled=!1;let t={target:this._target,addedNodes:[],removedNodes:[]},e=this.constructor.getFlattenedNodes(this._target),n=Dn(e,this._effectiveNodes);for(let e,r=0;r<n.length&&(e=n[r]);r++)for(let n,r=0;r<e.removed.length&&(n=e.removed[r]);r++)t.removedNodes.push(n);for(let r,s=0;s<n.length&&(r=n[s]);s++)for(let n=r.index;n<r.index+r.addedCount;n++)t.addedNodes.push(e[n]);this._effectiveNodes=e;let r=!1;return(t.addedNodes.length||t.removedNodes.length)&&(r=!0,this.callback.call(this._target,t)),r}_listenSlots(t){for(let e=0;e<t.length;e++){let n=t[e];Fn(n)&&n.addEventListener("slotchange",this._boundSchedule)}}_unlistenSlots(t){for(let e=0;e<t.length;e++){let n=t[e];Fn(n)&&n.removeEventListener("slotchange",this._boundSchedule)}}};const zn=Element.prototype,jn=zn.matches||zn.matchesSelector||zn.mozMatchesSelector||zn.msMatchesSelector||zn.oMatchesSelector||zn.webkitMatchesSelector,Bn=function(t,e){return jn.call(t,e)};class Jn{constructor(t){this.node=t}observeNodes(t){return new Hn(this.node,t)}unobserveNodes(t){t.disconnect()}notifyObserver(){}deepContains(t){if(I(this.node).contains(t))return!0;let e=t,n=t.ownerDocument;for(;e&&e!==n&&e!==this.node;)e=I(e).parentNode||I(e).host;return e===this.node}getOwnerRoot(){return I(this.node).getRootNode()}getDistributedNodes(){return"slot"===this.node.localName?I(this.node).assignedNodes({flatten:!0}):[]}getDestinationInsertionPoints(){let t=[],e=I(this.node).assignedSlot;for(;e;)t.push(e),e=I(e).assignedSlot;return t}importNode(t,e){let n=this.node instanceof Document?this.node:this.node.ownerDocument;return I(n).importNode(t,e)}getEffectiveChildNodes(){return Hn.getFlattenedNodes(this.node)}queryDistributedElements(t){let e=this.getEffectiveChildNodes(),n=[];for(let r,s=0,i=e.length;s<i&&(r=e[s]);s++)r.nodeType===Node.ELEMENT_NODE&&Bn(r,t)&&n.push(r);return n}get activeElement(){let t=this.node;return void 0!==t._activeElement?t._activeElement:t.activeElement}}function qn(t,e){for(let n=0;n<e.length;n++){let r=e[n];Object.defineProperty(t,r,{get:function(){return this.node[r]},configurable:!0})}}class Yn{constructor(t){this.event=t}get rootTarget(){return this.path[0]}get localTarget(){return this.event.target}get path(){return this.event.composedPath()}}Jn.prototype.cloneNode,Jn.prototype.appendChild,Jn.prototype.insertBefore,Jn.prototype.removeChild,Jn.prototype.replaceChild,Jn.prototype.setAttribute,Jn.prototype.removeAttribute,Jn.prototype.querySelector,Jn.prototype.querySelectorAll,Jn.prototype.parentNode,Jn.prototype.firstChild,Jn.prototype.lastChild,Jn.prototype.nextSibling,Jn.prototype.previousSibling,Jn.prototype.firstElementChild,Jn.prototype.lastElementChild,Jn.prototype.nextElementSibling,Jn.prototype.previousElementSibling,Jn.prototype.childNodes,Jn.prototype.children,Jn.prototype.classList,Jn.prototype.textContent,Jn.prototype.innerHTML;let $n=Jn;!function(t,e){for(let n=0;n<e.length;n++){let r=e[n];t[r]=function(){return this.node[r].apply(this.node,arguments)}}}(Jn.prototype,["cloneNode","appendChild","insertBefore","removeChild","replaceChild","setAttribute","removeAttribute","querySelector","querySelectorAll","attachShadow"]),qn(Jn.prototype,["parentNode","firstChild","lastChild","nextSibling","previousSibling","firstElementChild","lastElementChild","nextElementSibling","previousElementSibling","childNodes","children","classList","shadowRoot"]),function(t,e){for(let n=0;n<e.length;n++){let r=e[n];Object.defineProperty(t,r,{get:function(){return this.node[r]},set:function(t){this.node[r]=t},configurable:!0})}}(Jn.prototype,["textContent","innerHTML","className"]);const Un=function(t){if((t=t||document)instanceof $n)return t;if(t instanceof Yn)return t;let e=t.__domApi;return e||(e=t instanceof Event?new Yn(t):new $n(t),t.__domApi=e),e};const Vn="disable-upgrade",Xn=t=>{for(;t;){const e=Object.getOwnPropertyDescriptor(t,"observedAttributes");if(e)return e.get;t=Object.getPrototypeOf(t.prototype).constructor}return()=>[]};d((t=>{const e=Xe(t);let n=Xn(e);return class extends e{constructor(){super(),this.__isUpgradeDisabled}static get observedAttributes(){return n.call(this).concat(Vn)}_initializeProperties(){this.hasAttribute(Vn)?this.__isUpgradeDisabled=!0:super._initializeProperties()}_enableProperties(){this.__isUpgradeDisabled||super._enableProperties()}_canApplyPropertyDefault(t){return super._canApplyPropertyDefault(t)&&!(this.__isUpgradeDisabled&&this._isPropertyPending(t))}attributeChangedCallback(t,e,n,r){t==Vn?this.__isUpgradeDisabled&&null==n&&(super._initializeProperties(),this.__isUpgradeDisabled=!1,I(this).isConnected&&super.connectedCallback()):super.attributeChangedCallback(t,e,n,r)}connectedCallback(){this.__isUpgradeDisabled||super.connectedCallback()}disconnectedCallback(){this.__isUpgradeDisabled||super.disconnectedCallback()}}}));const Gn="disable-upgrade";const Wn=d((t=>{const e=En(Xe(t)),n=Xn(e),r={x:"pan-x",y:"pan-y",none:"none",all:"auto"};class s extends e{constructor(){super(),this.isAttached,this.__boundListeners,this._debouncers,this.__isUpgradeDisabled,this.__needsAttributesAtConnected,this._legacyForceObservedAttributes}static get importMeta(){return this.prototype.importMeta}created(){}__attributeReaction(t,e,n){(this.__dataAttributes&&this.__dataAttributes[t]||t===Gn)&&this.attributeChangedCallback(t,e,n,null)}setAttribute(t,e){super.setAttribute(t,e)}removeAttribute(t){super.removeAttribute(t)}static get observedAttributes(){return n.call(this).concat(Gn)}_enableProperties(){this.__isUpgradeDisabled||super._enableProperties()}_canApplyPropertyDefault(t){return super._canApplyPropertyDefault(t)&&!(this.__isUpgradeDisabled&&this._isPropertyPending(t))}connectedCallback(){this.__needsAttributesAtConnected&&this._takeAttributes(),this.__isUpgradeDisabled||(super.connectedCallback(),this.isAttached=!0,this.attached())}attached(){}disconnectedCallback(){this.__isUpgradeDisabled||(super.disconnectedCallback(),this.isAttached=!1,this.detached())}detached(){}attributeChangedCallback(t,e,n,r){e!==n&&(t==Gn?this.__isUpgradeDisabled&&null==n&&(this._initializeProperties(),this.__isUpgradeDisabled=!1,I(this).isConnected&&this.connectedCallback()):(super.attributeChangedCallback(t,e,n,r),this.attributeChanged(t,e,n)))}attributeChanged(t,e,n){}_initializeProperties(){if(x&&this.hasAttribute(Gn))this.__isUpgradeDisabled=!0;else{let t=Object.getPrototypeOf(this);t.hasOwnProperty(JSCompiler_renameProperty("__hasRegisterFinished",t))||(this._registered(),t.__hasRegisterFinished=!0),super._initializeProperties(),this.root=this,this.created(),this._applyListeners()}}_takeAttributes(){const t=this.attributes;for(let e=0,n=t.length;e<n;e++){const n=t[e];this.__attributeReaction(n.name,null,n.value)}}_registered(){}ready(){this._ensureAttributes(),super.ready()}_ensureAttributes(){}_applyListeners(){}serialize(t){return this._serializeValue(t)}deserialize(t,e){return this._deserializeValue(t,e)}reflectPropertyToAttribute(t,e,n){this._propertyToAttribute(t,e,n)}serializeValueToAttribute(t,e,n){this._valueToNodeAttribute(n||this,t,e)}extend(t,e){if(!t||!e)return t||e;let n=Object.getOwnPropertyNames(e);for(let r,s=0;s<n.length&&(r=n[s]);s++){let n=Object.getOwnPropertyDescriptor(e,r);n&&Object.defineProperty(t,r,n)}return t}mixin(t,e){for(let n in e)t[n]=e[n];return t}chainObject(t,e){return t&&e&&t!==e&&(t.__proto__=e),t}instanceTemplate(t){let e=this.constructor._contentForTemplate(t);return document.importNode(e,!0)}fire(t,e,n){n=n||{},e=null==e?{}:e;let r=new Event(t,{bubbles:void 0===n.bubbles||n.bubbles,cancelable:Boolean(n.cancelable),composed:void 0===n.composed||n.composed});r.detail=e;let s=n.node||this;return I(s).dispatchEvent(r),r}listen(t,e,n){t=t||this;let r=this.__boundListeners||(this.__boundListeners=new WeakMap),s=r.get(t);s||(s={},r.set(t,s));let i=e+n;s[i]||(s[i]=this._addMethodEventListenerToNode(t,e,n,this))}unlisten(t,e,n){t=t||this;let r=this.__boundListeners&&this.__boundListeners.get(t),s=e+n,i=r&&r[s];i&&(this._removeEventListenerFromNode(t,e,i),r[s]=null)}setScrollDirection(t,e){lt(e||this,r[t]||"auto")}$$(t){return this.root.querySelector(t)}get domHost(){let t=I(this).getRootNode();return t instanceof DocumentFragment?t.host:t}distributeContent(){Un(this)}getEffectiveChildNodes(){return Un(this).getEffectiveChildNodes()}queryDistributedElements(t){return Un(this).queryDistributedElements(t)}getEffectiveChildren(){return this.getEffectiveChildNodes().filter((function(t){return t.nodeType===Node.ELEMENT_NODE}))}getEffectiveTextContent(){let t=this.getEffectiveChildNodes(),e=[];for(let n,r=0;n=t[r];r++)n.nodeType!==Node.COMMENT_NODE&&e.push(n.textContent);return e.join("")}queryEffectiveChildren(t){let e=this.queryDistributedElements(t);return e&&e[0]}queryAllEffectiveChildren(t){return this.queryDistributedElements(t)}getContentChildNodes(t){let e=this.root.querySelector(t||"slot");return e?Un(e).getDistributedNodes():[]}getContentChildren(t){return this.getContentChildNodes(t).filter((function(t){return t.nodeType===Node.ELEMENT_NODE}))}isLightDescendant(t){const e=this;return e!==t&&I(e).contains(t)&&I(e).getRootNode()===I(t).getRootNode()}isLocalDescendant(t){return this.root===I(t).getRootNode()}scopeSubtree(t,e=!1){return null}getComputedStyleValue(t){return false.getComputedStyleValue(this,t)}debounce(t,e,n){return this._debouncers=this._debouncers||{},this._debouncers[t]=_.debounce(this._debouncers[t],n>0?o.after(n):h,e.bind(this))}isDebouncerActive(t){this._debouncers=this._debouncers||{};let e=this._debouncers[t];return!(!e||!e.isActive())}flushDebouncer(t){this._debouncers=this._debouncers||{};let e=this._debouncers[t];e&&e.flush()}cancelDebouncer(t){this._debouncers=this._debouncers||{};let e=this._debouncers[t];e&&e.cancel()}async(t,e){return e>0?o.run(t.bind(this),e):~h.run(t.bind(this))}cancelAsync(t){t<0?h.cancel(~t):o.cancel(t)}create(t,e){let n=document.createElement(t);if(e)if(n.setProperties)n.setProperties(e);else for(let t in e)n[t]=e[t];return n}elementMatches(t,e){return Bn(e||this,t)}toggleAttribute(t,e){let n=this;return 3===arguments.length&&(n=arguments[2]),1==arguments.length&&(e=!n.hasAttribute(t)),e?(I(n).setAttribute(t,""),!0):(I(n).removeAttribute(t),!1)}toggleClass(t,e,n){n=n||this,1==arguments.length&&(e=!n.classList.contains(t)),e?n.classList.add(t):n.classList.remove(t)}transform(t,e){(e=e||this).style.webkitTransform=t,e.style.transform=t}translate3d(t,e,n,r){r=r||this,this.transform("translate3d("+t+","+e+","+n+")",r)}arrayDelete(t,e){let n;if(Array.isArray(t)){if(n=t.indexOf(e),n>=0)return t.splice(n,1)}else{if(n=jt(this,t).indexOf(e),n>=0)return this.splice(t,n,1)}return null}_logger(t,e){switch(Array.isArray(e)&&1===e.length&&Array.isArray(e[0])&&(e=e[0]),t){case"log":case"warn":case"error":console[t](...e)}}_log(...t){this._logger("log",t)}_warn(...t){this._logger("warn",t)}_error(...t){this._logger("error",t)}_logf(t,...e){return["[%s::%s]",this.is,t,...e]}}return s.prototype.is="",s})),Zn={attached:!0,detached:!0,ready:!0,created:!0,beforeRegister:!0,registered:!0,attributeChanged:!0,listeners:!0,hostAttributes:!0},Kn={attached:!0,detached:!0,ready:!0,created:!0,beforeRegister:!0,registered:!0,attributeChanged:!0,behaviors:!0,_noAccessors:!0},Qn=Object.assign({listeners:!0,hostAttributes:!0,properties:!0,observers:!0},Kn);function tr(t,e){return ir({},Wn(e),t)}function er(t,e,n,r){!function(t,e,n){const r=t._noAccessors,s=Object.getOwnPropertyNames(t);for(let i=0;i<s.length;i++){let o=s[i];if(!(o in n))if(r)e[o]=t[o];else{let n=Object.getOwnPropertyDescriptor(t,o);n&&(n.configurable=!0,Object.defineProperty(e,o,n))}}}(e,t,r);for(let t in Zn)e[t]&&(n[t]=n[t]||[],n[t].push(e[t]))}function nr(t,e,n){e=e||[];for(let r=t.length-1;r>=0;r--){let s=t[r];s?Array.isArray(s)?nr(s,e):e.indexOf(s)<0&&(!n||n.indexOf(s)<0)&&e.unshift(s):console.warn("behavior is null, check for missing or 404 import")}return e}function rr(t,e){for(const n in e){const r=t[n],s=e[n];t[n]=!("value"in s)&&r&&"value"in r?Object.assign({value:r.value},s):s}}const sr=Wn(HTMLElement);function ir(t,e,n){let r;const s={};class i extends e{static _finalizeClass(){if(this.hasOwnProperty(JSCompiler_renameProperty("generatedFrom",this))){if(r)for(let t,e=0;e<r.length;e++)t=r[e],t.properties&&this.createProperties(t.properties),t.observers&&this.createObservers(t.observers,t.properties);t.properties&&this.createProperties(t.properties),t.observers&&this.createObservers(t.observers,t.properties),this._prepareTemplate()}else e._finalizeClass.call(this)}static get properties(){const e={};if(r)for(let t=0;t<r.length;t++)rr(e,r[t].properties);return rr(e,t.properties),e}static get observers(){let e=[];if(r)for(let t,n=0;n<r.length;n++)t=r[n],t.observers&&(e=e.concat(t.observers));return t.observers&&(e=e.concat(t.observers)),e}created(){super.created();const t=s.created;if(t)for(let e=0;e<t.length;e++)t[e].call(this)}_registered(){const t=i.prototype;if(!t.hasOwnProperty(JSCompiler_renameProperty("__hasRegisterFinished",t))){t.__hasRegisterFinished=!0,super._registered(),x&&o(t);const e=Object.getPrototypeOf(this);let n=s.beforeRegister;if(n)for(let t=0;t<n.length;t++)n[t].call(e);if(n=s.registered,n)for(let t=0;t<n.length;t++)n[t].call(e)}}_applyListeners(){super._applyListeners();const t=s.listeners;if(t)for(let e=0;e<t.length;e++){const n=t[e];if(n)for(let t in n)this._addMethodEventListenerToNode(this,t,n[t])}}_ensureAttributes(){const t=s.hostAttributes;if(t)for(let e=t.length-1;e>=0;e--){const n=t[e];for(let t in n)this._ensureAttribute(t,n[t])}super._ensureAttributes()}ready(){super.ready();let t=s.ready;if(t)for(let e=0;e<t.length;e++)t[e].call(this)}attached(){super.attached();let t=s.attached;if(t)for(let e=0;e<t.length;e++)t[e].call(this)}detached(){super.detached();let t=s.detached;if(t)for(let e=0;e<t.length;e++)t[e].call(this)}attributeChanged(t,e,n){super.attributeChanged();let r=s.attributeChanged;if(r)for(let s=0;s<r.length;s++)r[s].call(this,t,e,n)}}if(n){Array.isArray(n)||(n=[n]);let t=e.prototype.behaviors;r=nr(n,null,t),i.prototype.behaviors=t?t.concat(n):r}const o=e=>{r&&function(t,e,n){for(let r=0;r<e.length;r++)er(t,e[r],n,Qn)}(e,r,s),er(e,t,s,Kn)};return x||o(i.prototype),i.generatedFrom=t,i}let or;or=rn._mutablePropertyChange;const ar={properties:{mutableData:Boolean},_shouldPropertyChange(t,e,n){return or(this,t,e,n,this.mutableData)}},lr=function(t){let e;return e="function"==typeof t?t:lr.Class(t),t._legacyForceObservedAttributes&&(e.prototype._legacyForceObservedAttributes=t._legacyForceObservedAttributes),customElements.define(e.is,e),e};lr.Class=function(t,e){t||console.warn("Polymer.Class requires `info` argument");let n=e?e(sr):sr;return n=ir(t,n,t.behaviors),n.is=n.prototype.is=t.is,n};const hr={templatize(t,e){this._templatizerTemplate=t,this.ctor=gn(t,this,{mutableData:Boolean(e),parentModel:this._parentModel,instanceProps:this._instanceProps,forwardHostProp:this._forwardHostPropV2,notifyInstanceProp:this._notifyInstancePropV2})},stamp(t){return new this.ctor(t)},modelForElement(t){return bn(this._templatizerTemplate,t)}},cr=En(sn($e(HTMLElement)));customElements.define("dom-bind",class extends cr{static get observedAttributes(){return["mutable-data"]}constructor(){if(super(),A)throw new Error("strictTemplatePolicy: dom-bind not allowed");this.root=null,this.$=null,this.__children=null}attributeChangedCallback(t,e,n,r){this.mutableData=!0}connectedCallback(){this.style.display="none",this.render()}disconnectedCallback(){this.__removeChildren()}__insertChildren(){I(I(this).parentNode).insertBefore(this.root,this)}__removeChildren(){if(this.__children)for(let t=0;t<this.__children.length;t++)this.root.appendChild(this.__children[t])}render(){let t;if(!this.__children){if(t=t||this.querySelector("template"),!t){let e=new MutationObserver((()=>{if(t=this.querySelector("template"),!t)throw new Error("dom-bind requires a <template> child");e.disconnect(),this.render()}));return void e.observe(this,{childList:!0})}this.root=this._stampTemplate(t),this.$=this.root.$,this.__children=[];for(let t=this.root.firstChild;t;t=t.nextSibling)this.__children[this.__children.length]=t;this._enableProperties()}this.__insertChildren(),this.dispatchEvent(new CustomEvent("dom-change",{bubbles:!0,composed:!0}))}});let dr=d((t=>{let e=Xe(t);return class extends e{static get properties(){return{items:{type:Array},multi:{type:Boolean,value:!1},selected:{type:Object,notify:!0},selectedItem:{type:Object,notify:!0},toggle:{type:Boolean,value:!1}}}static get observers(){return["__updateSelection(multi, items.*)"]}constructor(){super(),this.__lastItems=null,this.__lastMulti=null,this.__selectedMap=null}__updateSelection(t,e){let n=e.path;if(n==JSCompiler_renameProperty("items",this)){let n=e.base||[],r=this.__lastItems;if(t!==this.__lastMulti&&this.clearSelection(),r){let t=Dn(n,r);this.__applySplices(t)}this.__lastItems=n,this.__lastMulti=t}else if(e.path==`${JSCompiler_renameProperty("items",this)}.splices`)this.__applySplices(e.value.indexSplices);else{let t=n.slice(`${JSCompiler_renameProperty("items",this)}.`.length),e=parseInt(t,10);t.indexOf(".")<0&&t==e&&this.__deselectChangedIdx(e)}}__applySplices(t){let e=this.__selectedMap;for(let n=0;n<t.length;n++){let r=t[n];e.forEach(((t,n)=>{t<r.index||(t>=r.index+r.removed.length?e.set(n,t+r.addedCount-r.removed.length):e.set(n,-1))}));for(let t=0;t<r.addedCount;t++){let n=r.index+t;e.has(this.items[n])&&e.set(this.items[n],n)}}this.__updateLinks();let n=0;e.forEach(((t,r)=>{t<0?(this.multi?this.splice(JSCompiler_renameProperty("selected",this),n,1):this.selected=this.selectedItem=null,e.delete(r)):n++}))}__updateLinks(){if(this.__dataLinkedPaths={},this.multi){let t=0;this.__selectedMap.forEach((e=>{e>=0&&this.linkPaths(`${JSCompiler_renameProperty("items",this)}.${e}`,`${JSCompiler_renameProperty("selected",this)}.${t++}`)}))}else this.__selectedMap.forEach((t=>{this.linkPaths(JSCompiler_renameProperty("selected",this),`${JSCompiler_renameProperty("items",this)}.${t}`),this.linkPaths(JSCompiler_renameProperty("selectedItem",this),`${JSCompiler_renameProperty("items",this)}.${t}`)}))}clearSelection(){this.__dataLinkedPaths={},this.__selectedMap=new Map,this.selected=this.multi?[]:null,this.selectedItem=null}isSelected(t){return this.__selectedMap.has(t)}isIndexSelected(t){return this.isSelected(this.items[t])}__deselectChangedIdx(t){let e=this.__selectedIndexForItemIndex(t);if(e>=0){let t=0;this.__selectedMap.forEach(((n,r)=>{e==t++&&this.deselect(r)}))}}__selectedIndexForItemIndex(t){let e=this.__dataLinkedPaths[`${JSCompiler_renameProperty("items",this)}.${t}`];if(e)return parseInt(e.slice(`${JSCompiler_renameProperty("selected",this)}.`.length),10)}deselect(t){let e=this.__selectedMap.get(t);if(e>=0){let n;this.__selectedMap.delete(t),this.multi&&(n=this.__selectedIndexForItemIndex(e)),this.__updateLinks(),this.multi?this.splice(JSCompiler_renameProperty("selected",this),n,1):this.selected=this.selectedItem=null}}deselectIndex(t){this.deselect(this.items[t])}select(t){this.selectIndex(this.items.indexOf(t))}selectIndex(t){let e=this.items[t];this.isSelected(e)?this.toggle&&this.deselectIndex(t):(this.multi||this.__selectedMap.clear(),this.__selectedMap.set(e,t),this.__updateLinks(),this.multi?this.push(JSCompiler_renameProperty("selected",this),e):this.selected=this.selectedItem=e)}}}))(tn);class _r extends dr{static get is(){return"array-selector"}static get template(){return null}}customElements.define(_r.is,_r);const ur="include";class pr extends HTMLElement{constructor(){super(),this._style=null}getStyle(){if(this._style)return this._style;const t=this.querySelector("style");if(!t)return null;this._style=t;const e=t.getAttribute(ur);return e&&(t.removeAttribute(ur),t.textContent=function(t){let e=t.trim().split(/\s+/),n="";for(let t=0;t<e.length;t++)n+=It(e[t]);return n}(e)+t.textContent),this.ownerDocument!==window.document&&window.document.head.appendChild(this),this._style}}window.customElements.define("custom-style",pr);const fr=Wn(HTMLElement).prototype;export{fr as Base,_ as Debouncer,vn as DomIf,wn as DomRepeat,Hn as FlattenedNodesObserver,ar as OptionalMutableDataBehavior,lr as Polymer,tn as PolymerElement,dn as TemplateInstanceBase,hr as Templatizer,In as afterNextRender,a as animationFrame,Sn as beforeNextRender,Dn as calculateSplices,$t as dashToCamelCase,d as dedupingMixin,Un as dom,p as enqueueDebouncer,en as flush,gt as gestures,jt as get,Ke as html,l as idlePeriod,Ft as matches,h as microTask,tr as mixinBehaviors,gn as templatize,o as timeOut,Rt as translate,w as useShadow}; \ No newline at end of file
diff --git a/third_party/polymer/v3_0/minify_polymer.py b/third_party/polymer/v3_0/minify_polymer.py index 007f3c9..ba46a1e5 100644 --- a/third_party/polymer/v3_0/minify_polymer.py +++ b/third_party/polymer/v3_0/minify_polymer.py
@@ -59,8 +59,9 @@ #'--comments', '/Copyright|license|LICENSE/', # Declare global variables as false, to prune out unnecessary code. - '--define', 'window.ShadyDOM=false', + '--define', 'window.HTMLImports=false', '--define', 'window.ShadyCSS=false', + '--define', 'window.ShadyDOM=false', '--define', 'window.Polymer.legacyOptimizations=false', '--output', minified_js])
diff --git a/tools/cast3p/cast_core.version b/tools/cast3p/cast_core.version index 1111ead..2edf240 100644 --- a/tools/cast3p/cast_core.version +++ b/tools/cast3p/cast_core.version
@@ -1 +1 @@ -cast_20230321_1054_RC00 +cast_20230306_0601_RC04
diff --git a/tools/cast3p/runtime.version b/tools/cast3p/runtime.version index 2ea448da..230e423 100644 --- a/tools/cast3p/runtime.version +++ b/tools/cast3p/runtime.version
@@ -1 +1 @@ -350948 +352020
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml index 23f689a..0a683665 100644 --- a/tools/metrics/histograms/enums.xml +++ b/tools/metrics/histograms/enums.xml
@@ -62622,6 +62622,7 @@ <int value="520982116" label="BuiltInModuleAll:enabled"/> <int value="521992655" label="LauncherPlayStoreSearch:disabled"/> <int value="522013077" label="LinkCapturingAutoDisplayIntentPicker:disabled"/> + <int value="524557829" label="OmniboxSteadyStateTextColor:enabled"/> <int value="525286392" label="PageInfoAboutThisSiteNonMsbb:disabled"/> <int value="527140412" label="AndroidAutofillViewStructureWithFormHierarchyLayer:disabled"/> @@ -62882,7 +62883,6 @@ <int value="670404276" label="ImeUsEnglishModelUpdate:disabled"/> <int value="671427343" label="DnsOverHttpsWithIdentifiersReuseOldPolicy:enabled"/> - <int value="671800756" label="TabStripImprovements:enabled"/> <int value="672067370" label="InstallableAmbientBadgeMessage:enabled"/> <int value="672255299" label="WebAuthFlowInBrowserTab:disabled"/> <int value="672599279" label="WebUiSystemFont:disabled"/> @@ -65501,6 +65501,7 @@ <int value="2107406401" label="UseDnsHttpsSvcb:disabled"/> <int value="2107520493" label="PolicyMergeMultiSource:enabled"/> <int value="2107942975" label="ApnRevamp:disabled"/> + <int value="2109483186" label="OmniboxSteadyStateTextColor:disabled"/> <int value="2110109003" label="WebAssemblyRelaxedSimd:disabled"/> <int value="2110832901" label="OmniboxPedalsTranslationConsole:enabled"/> <int value="2112312086" label="EditContext:disabled"/>
diff --git a/tools/metrics/histograms/metadata/language/histograms.xml b/tools/metrics/histograms/metadata/language/histograms.xml index 2dbd7a25..49bba81 100644 --- a/tools/metrics/histograms/metadata/language/histograms.xml +++ b/tools/metrics/histograms/metadata/language/histograms.xml
@@ -79,7 +79,7 @@ </histogram> <histogram name="LanguageSettings.AppLanguagePrompt.Action" - enum="LanguageSettingsAppLanguagePromptAction" expires_after="2023-08-08"> + enum="LanguageSettingsAppLanguagePromptAction" expires_after="2023-09-22"> <owner>perrier@chromium.org</owner> <owner>chrome-language@google.com</owner> <summary> @@ -90,24 +90,8 @@ </summary> </histogram> -<histogram name="LanguageSettings.AppLanguagePrompt.HasTopULPMatch" - enum="BooleanYesNo" expires_after="2023-01-15"> - <obsolete> - Replaced by LanguageSettings.AppLanguagePrompt.HasTopULPMatchOrEmpty - </obsolete> - <owner>perrier@chromium.org</owner> - <owner>chrome-language@google.com</owner> - <summary> - Whether or not the Chrome UI language matches the top ULP language when the - AppLanguagePrompt should be shown. Logged when checking if the prompt should - be shown and it has not already been shown. - - Only the base languages are compared so pt-PT is considered equal to pt-BR. - </summary> -</histogram> - <histogram name="LanguageSettings.AppLanguagePrompt.IsOnline" - enum="BooleanYesNo" expires_after="2023-08-20"> + enum="BooleanYesNo" expires_after="2023-09-22"> <owner>perrier@chromium.org</owner> <owner>chrome-language@google.com</owner> <summary> @@ -121,7 +105,7 @@ </histogram> <histogram name="LanguageSettings.AppLanguagePrompt.IsTopLanguageSelected" - enum="BooleanYesNo" expires_after="2023-08-08"> + enum="BooleanYesNo" expires_after="2023-09-22"> <owner>perrier@chromium.org</owner> <owner>chrome-language@google.com</owner> <summary> @@ -135,7 +119,7 @@ </histogram> <histogram name="LanguageSettings.AppLanguagePrompt.Language" - enum="LocaleCodeISO639" expires_after="2023-08-31"> + enum="LocaleCodeISO639" expires_after="2023-09-22"> <owner>perrier@chromium.org</owner> <owner>chrome-language@google.com</owner> <summary> @@ -149,30 +133,8 @@ </summary> </histogram> -<histogram name="LanguageSettings.AppLanguagePrompt.OpenDuration.{ActionType}" - units="ms" expires_after="2022-06-30"> - <obsolete> - Deleted May 2022 - </obsolete> - <owner>perrier@chromium.org</owner> - <owner>chrome-language@google.com</owner> - <summary> - The length of time between showing the app lanugage prompt and the user - pressing {ActionType}. - - An action must be taken if the prompt is shown, unless the application is - closed while the prompt is displayed. Reported when the prompt is dismissed. - </summary> - <token key="ActionType"> - <variant name="Back" summary="system back"/> - <variant name="Cancel" summary="the cancel button"/> - <variant name="Change" summary="OK changing the app language"/> - <variant name="Same" summary="OK for the current app language"/> - </token> -</histogram> - <histogram name="LanguageSettings.AppLanguagePrompt.OtherLanguagesShown" - enum="BooleanYesNo" expires_after="2023-08-31"> + enum="BooleanYesNo" expires_after="2023-09-22"> <owner>perrier@chromium.org</owner> <owner>chrome-language@google.com</owner> <summary> @@ -184,7 +146,7 @@ </histogram> <histogram name="LanguageSettings.AppLanguagePrompt.TopLanguageCount" - units="count" expires_after="2023-03-19"> + units="count" expires_after="2023-09-22"> <owner>perrier@chromium.org</owner> <owner>chrome-language@google.com</owner> <summary> @@ -196,7 +158,7 @@ </histogram> <histogram name="LanguageSettings.AppLanguagePrompt.TopULPMatchStatus" - enum="ULPTopLanguageMatch" expires_after="2023-08-06"> + enum="ULPTopLanguageMatch" expires_after="2023-09-22"> <owner>perrier@chromium.org</owner> <owner>chrome-language@google.com</owner> <summary> @@ -273,20 +235,6 @@ </summary> </histogram> -<histogram name="LanguageUsage.ApplicationLanguage" enum="LanguageName" - expires_after="2023-03-19"> - <obsolete> - Removed in M109. Use UMA.SystemProfile.ApplicationLocale which is a - synthetic histogram that supports country codes. - </obsolete> - <owner>perrier@chromium.org</owner> - <owner>chrome-language@google.com</owner> - <summary> - Application languages used for UI recorded once per profile initialization. - Incognito profiles are ignored. - </summary> -</histogram> - <histogram name="LanguageUsage.MostFrequentPageLanguages" enum="LanguageName" expires_after="2023-08-08"> <owner>perrier@chromium.org</owner> @@ -408,7 +356,7 @@ <histogram name="LanguageUsage.ULP.Initiation.AcceptLanguagesPageLanguageOverlap.Base" - units="%" expires_after="2023-03-19"> + units="%" expires_after="2023-09-22"> <owner>perrier@google.com</owner> <owner>chrome-language@google.com</owner> <summary> @@ -422,25 +370,8 @@ </summary> </histogram> -<histogram name="LanguageUsage.ULP.Initiation.AcceptLanguagesULPOverlap" - units="%" expires_after="2022-07-22"> - <obsolete> - Replaced by LanguageUsage.ULP.Initiation.AcceptLanguagesULPOverlap.Base - </obsolete> - <owner>perrier@google.com</owner> - <owner>chrome-language@google.com</owner> - <summary> - The ratio of ULP languages in the user's Accept Languages over the total - number of languages in their LanguageProfile. This answers the question of - "How many of the predicted ULP languages have already been added by the - user?". - - Recorded once at startup when ULP is initialized. - </summary> -</histogram> - <histogram name="LanguageUsage.ULP.Initiation.AcceptLanguagesULPOverlap.Base" - units="%" expires_after="2023-08-08"> + units="%" expires_after="2023-09-22"> <owner>perrier@google.com</owner> <owner>chrome-language@google.com</owner> <summary> @@ -455,7 +386,7 @@ </histogram> <histogram name="LanguageUsage.ULP.Initiation.ChromeUILanguageInULP" - enum="ULPLanguageStatus" expires_after="2023-08-08"> + enum="ULPLanguageStatus" expires_after="2023-09-22"> <owner>perrier@google.com</owner> <owner>chrome-language@google.com</owner> <summary> @@ -468,7 +399,7 @@ </histogram> <histogram name="LanguageUsage.ULP.Initiation.Count" units="count" - expires_after="2023-08-08"> + expires_after="2023-09-22"> <owner>perrier@google.com</owner> <owner>chrome-language@google.com</owner> <summary> @@ -479,7 +410,7 @@ </histogram> <histogram name="LanguageUsage.ULP.Initiation.NeverLanguagesMissingFromULP" - enum="LocaleCodeISO639" expires_after="2023-08-08"> + enum="LocaleCodeISO639" expires_after="2023-09-22"> <owner>perrier@google.com</owner> <owner>chrome-language@google.com</owner> <summary> @@ -492,7 +423,7 @@ <histogram name="LanguageUsage.ULP.Initiation.NeverLanguagesMissingFromULP.Count" - units="count" expires_after="2023-08-08"> + units="count" expires_after="2023-09-22"> <owner>perrier@google.com</owner> <owner>chrome-language@google.com</owner> <summary> @@ -503,7 +434,7 @@ </histogram> <histogram name="LanguageUsage.ULP.Initiation.PageLanguagesMissingFromULP" - enum="LocaleCodeISO639" expires_after="2023-03-19"> + enum="LocaleCodeISO639" expires_after="2023-09-22"> <owner>perrier@google.com</owner> <owner>chrome-language@google.com</owner> <summary> @@ -515,7 +446,7 @@ <histogram name="LanguageUsage.ULP.Initiation.PageLanguagesMissingFromULP.Count" - units="count" expires_after="2023-03-19"> + units="count" expires_after="2023-09-22"> <owner>perrier@google.com</owner> <owner>chrome-language@google.com</owner> <summary> @@ -526,7 +457,7 @@ </histogram> <histogram name="LanguageUsage.ULP.Initiation.Status{AccountType}" - enum="ULPInitiationStatus" expires_after="2023-08-31"> + enum="ULPInitiationStatus" expires_after="2023-09-22"> <owner>perrier@google.com</owner> <owner>chrome-language@google.com</owner> <summary> @@ -542,7 +473,7 @@ </histogram> <histogram name="LanguageUsage.ULP.Initiation.TopAcceptLanguageInULP" - enum="ULPLanguageStatus" expires_after="2023-08-08"> + enum="ULPLanguageStatus" expires_after="2023-09-22"> <owner>perrier@google.com</owner> <owner>chrome-language@google.com</owner> <summary> @@ -555,7 +486,7 @@ </histogram> <histogram name="LanguageUsage.ULP.Initiation.TranslateTargetInULP" - enum="ULPLanguageStatus" expires_after="2023-08-08"> + enum="ULPLanguageStatus" expires_after="2023-09-22"> <owner>perrier@google.com</owner> <owner>chrome-language@google.com</owner> <summary>
diff --git a/tools/metrics/histograms/metadata/sb_client/histograms.xml b/tools/metrics/histograms/metadata/sb_client/histograms.xml index 42e11612..6accb18 100644 --- a/tools/metrics/histograms/metadata/sb_client/histograms.xml +++ b/tools/metrics/histograms/metadata/sb_client/histograms.xml
@@ -358,7 +358,7 @@ </histogram> <histogram name="SBClientDownload.MalwareDeepScanResult.{trigger}" - enum="SBClientDownloadCheckResult" expires_after="2023-04-28"> + enum="SBClientDownloadCheckResult" expires_after="2024-04-28"> <owner>drubery@chromium.org</owner> <owner>chrome-counter-abuse-alerts@google.com</owner> <summary>
diff --git a/tools/metrics/histograms/metadata/variations/histograms.xml b/tools/metrics/histograms/metadata/variations/histograms.xml index e74fe8a..f43c33af 100644 --- a/tools/metrics/histograms/metadata/variations/histograms.xml +++ b/tools/metrics/histograms/metadata/variations/histograms.xml
@@ -452,7 +452,7 @@ </histogram> <histogram name="Variations.SafeModeCachedFlags.Streak.Crashes" units="crashes" - expires_after="2023-09-03"> + expires_after="2024-03-25"> <owner>hnakashima@chromium.org</owner> <owner>src/chrome/browser/flags/OWNERS</owner> <summary> @@ -465,7 +465,7 @@ </histogram> <histogram name="Variations.SafeModeCachedFlags.{Event}" - enum="VariationsSafeModeCachedFlagsBehavior" expires_after="2023-05-01"> + enum="VariationsSafeModeCachedFlagsBehavior" expires_after="2024-03-25"> <owner>hnakashima@chromium.org</owner> <owner>src/chrome/browser/flags/OWNERS</owner> <summary>
diff --git a/tools/style_variable_generator/base_generator.py b/tools/style_variable_generator/base_generator.py index 6dcf344..b0352580 100644 --- a/tools/style_variable_generator/base_generator.py +++ b/tools/style_variable_generator/base_generator.py
@@ -45,19 +45,19 @@ # If specified, only generates the given mode. self.generate_single_mode = None - # If true, resolves all blend() colors to the RGBA values at - # compile time. - self.resolve_blended_colors = True - # A dictionary of options used to alter generator function. See # ./README.md for each generators list of options. self.generator_options = {} + # If true, resolves all blend() colors to the RGBA values at + # compile time. + def ShouldResolveBlendedColors(self): + return True + def GetInputFiles(self): return sorted(self.in_file_to_context.keys()) - def AddJSONFilesToModel(self, paths): '''Adds one or more JSON files to the model. ''' @@ -69,7 +69,7 @@ raise ValueError(f'Could not add {path}') from err self.model.PostProcess( - resolve_blended_colors=self.resolve_blended_colors) + resolve_blended_colors=self.ShouldResolveBlendedColors()) def AddJSONToModel(self, json_string, in_file=None): '''Adds a |json_string| with variable definitions to the model.
diff --git a/tools/style_variable_generator/color_mappings_generator.py b/tools/style_variable_generator/color_mappings_generator.py index 9bd4468..f15db0d 100644 --- a/tools/style_variable_generator/color_mappings_generator.py +++ b/tools/style_variable_generator/color_mappings_generator.py
@@ -18,10 +18,6 @@ def GetName(): return 'ColorMappings' - def __init__(self): - super().__init__() - self.resolve_blended_colors = False - def GetParameters(self): return { 'color_mappings': self._CreateMappings(), @@ -55,6 +51,9 @@ return globals + def ShouldResolveBlendedColors(self): + return False + def _CreateMappings(self): mappings = collections.defaultdict(list) for name, mode_values in self.model.colors.items():
diff --git a/tools/style_variable_generator/css_generator.py b/tools/style_variable_generator/css_generator.py index 49dc90f..55cf962 100644 --- a/tools/style_variable_generator/css_generator.py +++ b/tools/style_variable_generator/css_generator.py
@@ -44,7 +44,6 @@ def GetFilters(self): return { 'to_css_var_name': self.ToCSSVarName, - 'css_color': self._CSSColor, 'css_opacity': self._CSSOpacity, 'css_color_rgb': self.CSSColorRGB, 'process_simple_ref': self.ProcessSimpleRef, @@ -54,6 +53,8 @@ return { 'css_color_var': self.CSSColorVar, + 'needs_rgb_variant': + self.NeedsRGBVariant, 'in_files': self.GetInputFiles(), 'dark_mode_selector': @@ -64,6 +65,9 @@ Modes, } + def ShouldResolveBlendedColors(self): + return False + def AddGeneratedVars(self, var_names, variable): def AddVarNames(name, variations): for v in variations: @@ -129,25 +133,6 @@ return ('%f' % opacity.a).rstrip('0').rstrip('.') - def _CSSColor(self, c): - '''Returns the CSS color representation of |c|''' - assert (isinstance(c, Color)) - if c.var: - return 'var(%s)' % self.ToCSSVarName(c.var) - - if c.rgb_var: - if c.opacity.a != 1: - return 'rgba(var(%s-rgb), %g)' % (self.ToCSSVarName( - c.RGBVarToVar()), self._CSSOpacity(c.opacity)) - else: - return 'rgb(var(%s-rgb))' % self.ToCSSVarName(c.RGBVarToVar()) - - elif c.a != 1: - return 'rgba(%d, %d, %d, %g)' % (c.r, c.g, c.b, - self._CSSOpacity(c.opacity)) - else: - return 'rgb(%d, %d, %d)' % (c.r, c.g, c.b) - def CSSColorRGB(self, c): '''Returns the CSS rgb representation of |c|''' if c.var: @@ -158,12 +143,35 @@ return '%d, %d, %d' % (c.r, c.g, c.b) - def CSSColorVar(self, name, color): + def CSSBlendInputColor(self, c): + '''Resolves a color for use in a color-mix call.''' + return 'rgb(%s)' % self.CSSColorRGB(c) + + def ExtractOpacity(self, c, mode): + if c.var: + return self.ExtractOpacity(self.model.colors.Resolve(c.var, mode), + mode) + if c.opacity: + return self.model.opacities.ResolveOpacity(c.opacity, mode).a * 100 + + # If we don't have opacity information assume we want to blend 100%. + return 100 + + def CSSColorVar(self, name, color, mode): '''Returns the CSS color representation given a color name and color''' + if color.blended_colors: + return 'color-mix(in srgb, %s %s%%, %s)' % ( + self.CSSBlendInputColor(color.blended_colors[0]), + self.ExtractOpacity(color.blended_colors[0], mode), + self.CSSBlendInputColor(color.blended_colors[1])) if color.var: return 'var(%s)' % self.ToCSSVarName(color.var) + if color.opacity and color.opacity.a != 1: return 'rgba(var(%s-rgb), %s)' % (self.ToCSSVarName(name), self._CSSOpacity(color.opacity)) - else: - return 'rgb(var(%s-rgb))' % self.ToCSSVarName(name) + + return 'rgb(var(%s-rgb))' % self.ToCSSVarName(name) + + def NeedsRGBVariant(self, color): + return not color.blended_colors
diff --git a/tools/style_variable_generator/templates/css_generator.tmpl b/tools/style_variable_generator/templates/css_generator.tmpl index 003e30977..46cce07 100644 --- a/tools/style_variable_generator/templates/css_generator.tmpl +++ b/tools/style_variable_generator/templates/css_generator.tmpl
@@ -14,8 +14,10 @@ } {% macro render_variables_for_mode(mode) -%} {%- for model_name, color in colors[mode].items() %} + {%- if needs_rgb_variant(color) %} {{model_name | to_css_var_name}}-rgb: {{color | css_color_rgb}}; - {{model_name | to_css_var_name}}: {{css_color_var(model_name, color)}}; + {%- endif %} + {{model_name | to_css_var_name}}: {{css_color_var(model_name, color, mode)}}; {% endfor %} {%- for name, value in opacities[mode].items() %}
diff --git a/tools/style_variable_generator/templates/ts_generator.tmpl b/tools/style_variable_generator/templates/ts_generator.tmpl index 69747171..ac773a8 100644 --- a/tools/style_variable_generator/templates/ts_generator.tmpl +++ b/tools/style_variable_generator/templates/ts_generator.tmpl
@@ -11,8 +11,10 @@ {%- endif %} {% macro render_variables_as_css(mode) -%} {%- for model_name, color in colors[mode].items() %} + {%- if needs_rgb_variant(color) %} {{model_name | to_css_var_name}}-rgb: {{color | css_color_rgb}}; - {{model_name | to_css_var_name}}: {{css_color_var(model_name, color)}}; + {%- endif %} + {{model_name | to_css_var_name}}: {{css_color_var(model_name, color, mode)}}; {% endfor %} {%- for name, value in opacities[mode].items() %}
diff --git a/tools/style_variable_generator/tests/blend_colors_test.json5 b/tools/style_variable_generator/tests/blend_colors_test.json5 index d1c136a..08564ad1 100644 --- a/tools/style_variable_generator/tests/blend_colors_test.json5 +++ b/tools/style_variable_generator/tests/blend_colors_test.json5
@@ -11,7 +11,7 @@ }, highlight_color_hover: { light: "rgba($black.rgb, 0.2)", - dark: "rgba($white.rgb, 0.2)", + dark: "rgba($white.rgb, 0.4)", }, foo_color: { light: "blend($highlight_color_hover, $bg_color)",
diff --git a/tools/style_variable_generator/tests/goldens/blend_colors_test_expected.css b/tools/style_variable_generator/tests/goldens/blend_colors_test_expected.css index f8eda87..5c4d1644 100644 --- a/tools/style_variable_generator/tests/goldens/blend_colors_test_expected.css +++ b/tools/style_variable_generator/tests/goldens/blend_colors_test_expected.css
@@ -20,11 +20,9 @@ --cros-highlight-color-hover-rgb: 0, 0, 0; --cros-highlight-color-hover: rgba(var(--cros-highlight-color-hover-rgb), 0.2); - --cros-foo-color-rgb: 204, 204, 204; - --cros-foo-color: rgb(var(--cros-foo-color-rgb)); + --cros-foo-color: color-mix(in srgb, rgb(var(--cros-highlight-color-hover-rgb)) 20.0%, rgb(var(--cros-bg-color-rgb))); - --cros-bar-color-rgb: 204, 204, 204; - --cros-bar-color: rgb(var(--cros-bar-color-rgb)); + --cros-bar-color: color-mix(in srgb, rgb(var(--cros-highlight-color-hover-rgb)) 20.0%, rgb(var(--cros-bg-color-rgb))); --cros-disabled-opacity: 0.38; } @@ -35,12 +33,10 @@ --cros-bg-color: var(--google-grey-900); --cros-highlight-color-hover-rgb: 255, 255, 255; - --cros-highlight-color-hover: rgba(var(--cros-highlight-color-hover-rgb), 0.2); + --cros-highlight-color-hover: rgba(var(--cros-highlight-color-hover-rgb), 0.4); - --cros-foo-color-rgb: 77, 77, 80; - --cros-foo-color: rgb(var(--cros-foo-color-rgb)); + --cros-foo-color: color-mix(in srgb, rgb(var(--cros-highlight-color-hover-rgb)) 40.0%, rgb(var(--cros-bg-color-rgb))); - --cros-bar-color-rgb: 77, 77, 80; - --cros-bar-color: rgb(var(--cros-bar-color-rgb)); + --cros-bar-color: color-mix(in srgb, rgb(var(--cros-highlight-color-hover-rgb)) 40.0%, rgb(var(--cros-bg-color-rgb))); } }
diff --git a/tools/style_variable_generator/tests/goldens/colors_test_custom_dark_toggle_expected.css b/tools/style_variable_generator/tests/goldens/colors_test_custom_dark_toggle_expected.css index 14aea06..8204029 100644 --- a/tools/style_variable_generator/tests/goldens/colors_test_custom_dark_toggle_expected.css +++ b/tools/style_variable_generator/tests/goldens/colors_test_custom_dark_toggle_expected.css
@@ -35,8 +35,7 @@ --cros-toggle-color-rgb: var(--cros-text-color-primary-rgb); --cros-toggle-color: rgba(var(--cros-toggle-color-rgb), var(--cros-disabled-opacity)); - --cros-bg-color-elevation-1-rgb: 41, 42, 45; - --cros-bg-color-elevation-1: rgb(var(--cros-bg-color-elevation-1-rgb)); + --cros-bg-color-elevation-1: color-mix(in srgb, rgb(255, 255, 255) 4.0%, rgb(var(--google-grey-900-rgb))); --cros-reference-opacity: 1; }
diff --git a/tools/style_variable_generator/tests/goldens/colors_test_dark_only_expected.css b/tools/style_variable_generator/tests/goldens/colors_test_dark_only_expected.css index e3bc9656..3777c38 100644 --- a/tools/style_variable_generator/tests/goldens/colors_test_dark_only_expected.css +++ b/tools/style_variable_generator/tests/goldens/colors_test_dark_only_expected.css
@@ -20,8 +20,7 @@ --cros-toggle-color-rgb: var(--cros-text-color-primary-rgb); --cros-toggle-color: rgba(var(--cros-toggle-color-rgb), var(--cros-disabled-opacity)); - --cros-bg-color-elevation-1-rgb: 41, 42, 45; - --cros-bg-color-elevation-1: rgb(var(--cros-bg-color-elevation-1-rgb)); + --cros-bg-color-elevation-1: color-mix(in srgb, rgb(255, 255, 255) 4.0%, rgb(var(--google-grey-900-rgb))); --cros-disabled-opacity: 0.38;
diff --git a/tools/style_variable_generator/tests/goldens/colors_test_expected.css b/tools/style_variable_generator/tests/goldens/colors_test_expected.css index ee4fe3d..faad3cd 100644 --- a/tools/style_variable_generator/tests/goldens/colors_test_expected.css +++ b/tools/style_variable_generator/tests/goldens/colors_test_expected.css
@@ -36,8 +36,7 @@ --cros-toggle-color-rgb: var(--cros-text-color-primary-rgb); --cros-toggle-color: rgba(var(--cros-toggle-color-rgb), var(--cros-disabled-opacity)); - --cros-bg-color-elevation-1-rgb: 41, 42, 45; - --cros-bg-color-elevation-1: rgb(var(--cros-bg-color-elevation-1-rgb)); + --cros-bg-color-elevation-1: color-mix(in srgb, rgb(255, 255, 255) 4.0%, rgb(var(--google-grey-900-rgb))); --cros-reference-opacity: 1; }
diff --git a/tools/style_variable_generator/tests/goldens/colors_test_include_style_sheet_expected.ts b/tools/style_variable_generator/tests/goldens/colors_test_include_style_sheet_expected.ts index ff60c6cb..2ea858466 100644 --- a/tools/style_variable_generator/tests/goldens/colors_test_include_style_sheet_expected.ts +++ b/tools/style_variable_generator/tests/goldens/colors_test_include_style_sheet_expected.ts
@@ -47,8 +47,7 @@ --cros-toggle-color-rgb: var(--cros-text-color-primary-rgb); --cros-toggle-color: rgba(var(--cros-toggle-color-rgb), var(--cros-disabled-opacity)); - --cros-bg-color-elevation-1-rgb: 41, 42, 45; - --cros-bg-color-elevation-1: rgb(var(--cros-bg-color-elevation-1-rgb)); + --cros-bg-color-elevation-1: color-mix(in srgb, rgb(255, 255, 255) 4.0%, rgb(var(--google-grey-900-rgb))); --cros-reference-opacity: 1; ` : '';
diff --git a/tools/style_variable_generator/tests/goldens/colors_test_typography_and_untyped_css_expected.ts b/tools/style_variable_generator/tests/goldens/colors_test_typography_and_untyped_css_expected.ts index cb6f504..987b229 100644 --- a/tools/style_variable_generator/tests/goldens/colors_test_typography_and_untyped_css_expected.ts +++ b/tools/style_variable_generator/tests/goldens/colors_test_typography_and_untyped_css_expected.ts
@@ -49,8 +49,7 @@ --cros-toggle-color-rgb: var(--cros-text-color-primary-rgb); --cros-toggle-color: rgba(var(--cros-toggle-color-rgb), var(--cros-disabled-opacity)); - --cros-bg-color-elevation-1-rgb: 41, 42, 45; - --cros-bg-color-elevation-1: rgb(var(--cros-bg-color-elevation-1-rgb)); + --cros-bg-color-elevation-1: color-mix(in srgb, rgb(255, 255, 255) 4.0%, rgb(var(--google-grey-900-rgb))); --cros-reference-opacity: 1; ` : '';
diff --git a/tools/style_variable_generator/tests/goldens/colors_test_typography_expected.ts b/tools/style_variable_generator/tests/goldens/colors_test_typography_expected.ts index 0c75811..c7202de 100644 --- a/tools/style_variable_generator/tests/goldens/colors_test_typography_expected.ts +++ b/tools/style_variable_generator/tests/goldens/colors_test_typography_expected.ts
@@ -48,8 +48,7 @@ --cros-toggle-color-rgb: var(--cros-text-color-primary-rgb); --cros-toggle-color: rgba(var(--cros-toggle-color-rgb), var(--cros-disabled-opacity)); - --cros-bg-color-elevation-1-rgb: 41, 42, 45; - --cros-bg-color-elevation-1: rgb(var(--cros-bg-color-elevation-1-rgb)); + --cros-bg-color-elevation-1: color-mix(in srgb, rgb(255, 255, 255) 4.0%, rgb(var(--google-grey-900-rgb))); --cros-reference-opacity: 1; ` : '';
diff --git a/tools/style_variable_generator/tests/goldens/colors_test_untyped_css_expected.ts b/tools/style_variable_generator/tests/goldens/colors_test_untyped_css_expected.ts index 1cb38dc..5f1667a 100644 --- a/tools/style_variable_generator/tests/goldens/colors_test_untyped_css_expected.ts +++ b/tools/style_variable_generator/tests/goldens/colors_test_untyped_css_expected.ts
@@ -48,8 +48,7 @@ --cros-toggle-color-rgb: var(--cros-text-color-primary-rgb); --cros-toggle-color: rgba(var(--cros-toggle-color-rgb), var(--cros-disabled-opacity)); - --cros-bg-color-elevation-1-rgb: 41, 42, 45; - --cros-bg-color-elevation-1: rgb(var(--cros-bg-color-elevation-1-rgb)); + --cros-bg-color-elevation-1: color-mix(in srgb, rgb(255, 255, 255) 4.0%, rgb(var(--google-grey-900-rgb))); --cros-reference-opacity: 1; ` : '';
diff --git a/tools/style_variable_generator/tests/goldens/colors_tokens_test_expected.css b/tools/style_variable_generator/tests/goldens/colors_tokens_test_expected.css index 89b2e8b..2826742 100644 --- a/tools/style_variable_generator/tests/goldens/colors_tokens_test_expected.css +++ b/tools/style_variable_generator/tests/goldens/colors_tokens_test_expected.css
@@ -42,8 +42,7 @@ --cros-sys-primary_container-rgb: var(--cros-ref-primary50-rgb); --cros-sys-primary_container: rgba(var(--cros-sys-primary_container-rgb), var(--cros-sys-disabled_opacity)); - --cros-sys-on_primary_container-rgb: 41, 42, 45; - --cros-sys-on_primary_container: rgb(var(--cros-sys-on_primary_container-rgb)); + --cros-sys-on_primary_container: color-mix(in srgb, rgb(255, 255, 255) 4.0%, rgb(var(--cros-ref-primary50-rgb))); --cros-sys-reference_opacity: 1; }
diff --git a/tools/style_variable_generator/tests/goldens/suppress_sources_comment_test_expected.css b/tools/style_variable_generator/tests/goldens/suppress_sources_comment_test_expected.css index 9ff3308..768d284 100644 --- a/tools/style_variable_generator/tests/goldens/suppress_sources_comment_test_expected.css +++ b/tools/style_variable_generator/tests/goldens/suppress_sources_comment_test_expected.css
@@ -31,8 +31,7 @@ --cros-toggle-color-rgb: var(--cros-text-color-primary-rgb); --cros-toggle-color: rgba(var(--cros-toggle-color-rgb), var(--cros-disabled-opacity)); - --cros-bg-color-elevation-1-rgb: 41, 42, 45; - --cros-bg-color-elevation-1: rgb(var(--cros-bg-color-elevation-1-rgb)); + --cros-bg-color-elevation-1: color-mix(in srgb, rgb(255, 255, 255) 4.0%, rgb(var(--google-grey-900-rgb))); --cros-reference-opacity: 1; }
diff --git a/tools/style_variable_generator/views_generator.py b/tools/style_variable_generator/views_generator.py index cf35c993..25a09d4 100644 --- a/tools/style_variable_generator/views_generator.py +++ b/tools/style_variable_generator/views_generator.py
@@ -57,6 +57,11 @@ return globals + def ShouldResolveBlendedColors(self): + # CSSGenerator sets this to false, we set it back to true since + # views does not do runtime blends. + return True + def _CreateColorList(self): color_list = [] for name, mode_values in self.model.colors.items():
diff --git a/ui/base/webui/web_ui_util.cc b/ui/base/webui/web_ui_util.cc index 265824de..48aae8c 100644 --- a/ui/base/webui/web_ui_util.cc +++ b/ui/base/webui/web_ui_util.cc
@@ -19,6 +19,7 @@ #include "ui/base/l10n/l10n_util.h" #include "ui/base/resource/resource_bundle.h" #include "ui/base/template_expressions.h" +#include "ui/base/ui_base_features.h" #include "ui/base/window_open_disposition.h" #include "ui/base/window_open_disposition_utils.h" #include "ui/gfx/codec/png_codec.h" @@ -37,10 +38,19 @@ // Generous cap to guard against out-of-memory issues. constexpr float kMaxScaleFactor = 1000.0f; +std::string GetFontFamilyMd() { + if (base::FeatureList::IsEnabled(features::kWebUiSystemFont)) { + return GetFontFamily(); + } + + return "Roboto, " + GetFontFamily(); +} + std::string GetWebUiCssTextDefaults(const std::string& css_template) { ui::TemplateReplacements placeholders; placeholders["textdirection"] = GetTextDirection(); placeholders["fontfamily"] = GetFontFamily(); + placeholders["fontfamilyMd"] = GetFontFamilyMd(); placeholders["fontsize"] = GetFontSize(); return ui::ReplaceTemplateExpressions(css_template, placeholders); } @@ -174,6 +184,7 @@ void SetLoadTimeDataDefaults(const std::string& app_locale, base::Value::Dict* localized_strings) { localized_strings->Set("fontfamily", GetFontFamily()); + localized_strings->Set("fontfamilyMd", GetFontFamilyMd()); localized_strings->Set("fontsize", GetFontSize()); localized_strings->Set("language", l10n_util::GetLanguage(app_locale)); localized_strings->Set("textdirection", GetTextDirection()); @@ -182,6 +193,7 @@ void SetLoadTimeDataDefaults(const std::string& app_locale, ui::TemplateReplacements* replacements) { (*replacements)["fontfamily"] = GetFontFamily(); + (*replacements)["fontfamilyMd"] = GetFontFamilyMd(); (*replacements)["fontsize"] = GetFontSize(); (*replacements)["language"] = l10n_util::GetLanguage(app_locale); (*replacements)["textdirection"] = GetTextDirection();
diff --git a/ui/chromeos/styles/cros_sys_colors.json5 b/ui/chromeos/styles/cros_sys_colors.json5 index 12eb84c6..40791cf 100644 --- a/ui/chromeos/styles/cros_sys_colors.json5 +++ b/ui/chromeos/styles/cros_sys_colors.json5
@@ -132,7 +132,7 @@ light: '$cros.ref.neutral99', dark: '$cros.ref.neutral10', }, - 'app-base-elevated': { + 'base-elevated': { light: '$cros.ref.neutralvariant100', /* In dark mode we layer primary80 @ 11% ontop of neutral80 @ 2% ontop of neutral 10. */ dark: 'blend(rgba($cros.ref.primary80.rgb, .11), blend(rgba($cros.ref.neutral80.rgb, .02), $cros.ref.neutral10))', @@ -198,11 +198,11 @@ dark: 'rgba($cros.ref.neutralvariant0.rgb, 0.14)', }, - 'input-field-light': { + 'input-field-on-shaded': { light: '$cros.ref.neutral99', dark: 'rgba($cros.ref.neutral50.rgb, 0.4)' }, - 'input-field-dark': { + 'input-field-on-base': { light: '$cros.ref.neutral95', dark: 'rgba($cros.ref.neutral0.rgb, 0.6)' },
diff --git a/ui/file_manager/file_manager/BUILD.gn b/ui/file_manager/file_manager/BUILD.gn index 434bd25..d2596f1c 100644 --- a/ui/file_manager/file_manager/BUILD.gn +++ b/ui/file_manager/file_manager/BUILD.gn
@@ -39,6 +39,7 @@ # CSS: "foreground/css/combobutton.css", + "foreground/css/combobutton_gm3.css", "foreground/css/common.css", "foreground/css/file_manager.css", "foreground/css/file_manager_gm3.css",
diff --git a/ui/file_manager/file_manager/common/js/entry_utils.ts b/ui/file_manager/file_manager/common/js/entry_utils.ts index 0e6ced4..b590657 100644 --- a/ui/file_manager/file_manager/common/js/entry_utils.ts +++ b/ui/file_manager/file_manager/common/js/entry_utils.ts
@@ -113,6 +113,40 @@ rootType === VolumeManagerCommon.RootType.DRIVE_OFFLINE; } +/** + * Returns true if fileData's entry is inside any part of Drive 'My Drive'. + */ +export function isEntryInsideMyDrive(fileData: FileData): boolean { + const {rootType} = fileData; + return !!rootType && rootType === VolumeManagerCommon.RootType.DRIVE; +} + +/** + * Returns true if fileData's entry is inside any part of Drive 'Computers'. + */ +export function isEntryInsideComputers(fileData: FileData): boolean { + const {rootType} = fileData; + return !!rootType && + (rootType === VolumeManagerCommon.RootType.COMPUTERS_GRAND_ROOT || + rootType === VolumeManagerCommon.RootType.COMPUTER); +} + +/** + * Returns true if fileData's entry is inside any part of Drive. + */ +export function isEntryInsideDrive(fileData: FileData): boolean { + const {rootType} = fileData; + return !!rootType && + (rootType === VolumeManagerCommon.RootType.DRIVE || + rootType === VolumeManagerCommon.RootType.SHARED_DRIVES_GRAND_ROOT || + rootType === VolumeManagerCommon.RootType.SHARED_DRIVE || + rootType === VolumeManagerCommon.RootType.COMPUTERS_GRAND_ROOT || + rootType === VolumeManagerCommon.RootType.COMPUTER || + rootType === VolumeManagerCommon.RootType.DRIVE_OFFLINE || + rootType === VolumeManagerCommon.RootType.DRIVE_SHARED_WITH_ME || + rootType === VolumeManagerCommon.RootType.DRIVE_FAKE_ROOT); +} + /** Sort the entries based on the filter and the names. */ export function sortEntries( parentEntry: Entry|FilesAppEntry,
diff --git a/ui/file_manager/file_manager/common/js/entry_utils_unittest.ts b/ui/file_manager/file_manager/common/js/entry_utils_unittest.ts new file mode 100644 index 0000000..474ac56 --- /dev/null +++ b/ui/file_manager/file_manager/common/js/entry_utils_unittest.ts
@@ -0,0 +1,71 @@ +// 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. + +import {assertFalse, assertTrue} from 'chrome://webui-test/chromeos/chai_assert.js'; + +import {setUpFileManagerOnWindow} from '../../state/for_tests.js'; +import {convertEntryToFileData} from '../../state/reducers/all_entries.js'; + +import {isEntryInsideDrive, isEntryInsideMyDrive} from './entry_utils.js'; +import {EntryList} from './files_app_entry_types.js'; +import {MockFileSystem} from './mock_entry.js'; +import {VolumeManagerCommon} from './volume_manager_types.js'; + +export function setUp() { + setUpFileManagerOnWindow(); +} + +/** + * Test private methods: isEntryInsideDrive_() and isEntryInsideMyDrive_(), + * which should return true when inside My Drive and any of its sub-directories; + * Should return false for everything else, including within Team Drive. + */ +export function testInsideMyDriveAndInsideDrive() { + // Add sub folders for MyFiles and Drive. + const {volumeManager} = window.fileManager; + const myFilesFs = + volumeManager + .getCurrentProfileVolumeInfo( + VolumeManagerCommon.VolumeType.DOWNLOADS)!.fileSystem as + MockFileSystem; + const driveFs = volumeManager + .getCurrentProfileVolumeInfo( + VolumeManagerCommon.VolumeType.DRIVE)!.fileSystem as + MockFileSystem; + myFilesFs.populate(['/folder1/']); + driveFs.populate(['/root/folder1']); + const driveRootEntryList = + new EntryList('Drive root', VolumeManagerCommon.RootType.DRIVE_FAKE_ROOT); + + // Convert entry into FileData. + const driveRootFileData = convertEntryToFileData(driveRootEntryList); + const myDrivesFileData = convertEntryToFileData(driveFs.entries['/root']); + const teamDrivesFileData = + convertEntryToFileData(driveFs.entries['/team_drives']); + const computersFileData = + convertEntryToFileData(driveFs.entries['/Computers']); + const myFilesFileData = convertEntryToFileData(myFilesFs.entries['/']); + const myFilesFolder1FileData = + convertEntryToFileData(myFilesFs.entries['/folder1']); + const myDrivesFolder1FileData = + convertEntryToFileData(driveFs.entries['/root/folder1']); + + // insideMyDrive + assertFalse(isEntryInsideMyDrive(driveRootFileData), 'Drive root'); + assertTrue(isEntryInsideMyDrive(myDrivesFileData), 'My Drives root'); + assertFalse(isEntryInsideMyDrive(teamDrivesFileData), 'Team Drives root'); + assertFalse(isEntryInsideMyDrive(computersFileData), 'Computers root'); + assertFalse(isEntryInsideMyDrive(myFilesFileData), 'MyFiles root'); + assertFalse(isEntryInsideMyDrive(myFilesFolder1FileData), 'MyFiles folder1'); + assertTrue( + isEntryInsideMyDrive(myDrivesFolder1FileData), 'My Drives folder1'); + // insideDrive + assertTrue(isEntryInsideDrive(driveRootFileData), 'Drive root'); + assertTrue(isEntryInsideDrive(myDrivesFileData), 'My Drives root'); + assertTrue(isEntryInsideDrive(teamDrivesFileData), 'Team Drives root'); + assertTrue(isEntryInsideDrive(computersFileData), 'Computers root'); + assertFalse(isEntryInsideDrive(myFilesFileData), 'MyFiles root'); + assertFalse(isEntryInsideMyDrive(myFilesFolder1FileData), 'MyFiles folder1'); + assertTrue(isEntryInsideDrive(myDrivesFolder1FileData), 'My Drives folder1'); +}
diff --git a/ui/file_manager/file_manager/containers/directory_tree_container.ts b/ui/file_manager/file_manager/containers/directory_tree_container.ts index 852d85f..1dc8e8b 100644 --- a/ui/file_manager/file_manager/containers/directory_tree_container.ts +++ b/ui/file_manager/file_manager/containers/directory_tree_container.ts
@@ -6,12 +6,11 @@ import {CrButtonElement} from 'chrome://resources/cr_elements/cr_button/cr_button.js'; import {maybeShowTooltip} from '../common/js/dom_utils.js'; -import {isGrandRootEntryInDrives, isMyFilesEntry, isVolumeEntry} from '../common/js/entry_utils.js'; +import {isEntryInsideComputers, isEntryInsideDrive, isEntryInsideMyDrive, isGrandRootEntryInDrives, isMyFilesEntry, isVolumeEntry} from '../common/js/entry_utils.js'; import {EntryList, VolumeEntry} from '../common/js/files_app_entry_types.js'; import {metrics} from '../common/js/metrics.js'; import {strf} from '../common/js/util.js'; import {VolumeManagerCommon} from '../common/js/volume_manager_types.js'; -import {FilesAppEntry} from '../externs/files_app_entry_interfaces.js'; import {FileData, NavigationKey, NavigationRoot, NavigationType, PropStatus, State} from '../externs/ts/state.js'; import {VolumeManager} from '../externs/volume_manager.js'; import {constants} from '../foreground/js/constants.js'; @@ -21,6 +20,7 @@ import {changeDirectory} from '../state/actions/current_directory.js'; import {refreshNavigationRoots, updateNavigationEntry} from '../state/actions/navigation.js'; import {readSubDirectories} from '../state/actions_producers/all_entries.js'; +import {convertEntryToFileData} from '../state/reducers/all_entries.js'; import {driveRootEntryListKey} from '../state/reducers/volumes.js'; import {getEntry, getFileData, getStore, Store} from '../state/store.js'; import {TreeSelectedChangedEvent, XfTree} from '../widgets/xf_tree.js'; @@ -275,16 +275,13 @@ // Handle navigation items backed up by a file entry. const fileData = newData as FileData; - const entry = fileData.entry; element.label = fileData.label; if (navigationRoot) { element.separator = navigationRoot.separator; } this.setItemIcon_(element, fileData, navigationRoot); - element.disabled = ('disabled' in entry && entry.disabled) ? - entry.disabled as boolean : - false; + element.disabled = fileData.disabled; // Add eject button for ejectable item. if (fileData.isEjectable) { @@ -292,9 +289,9 @@ } // Fetch metadata if the entry supports Drive specific share icon. - if (this.shouldSupportDriveSpecificIcons_(fileData.entry)) { + if (this.shouldSupportDriveSpecificIcons_(fileData)) { this.metadataModel_.get( - [fileData.entry], DRIVE_ENTRY_METADATA_PROPERTY_NAMES); + [fileData.entry as Entry], DRIVE_ENTRY_METADATA_PROPERTY_NAMES); } if (!navigationRoot?.type || @@ -361,8 +358,7 @@ element.icon = fileData.icon; } // For drive item, update icon based on the metadata. - if (this.shouldSupportDriveSpecificIcons_(fileData.entry) && - fileData.metadata) { + if (this.shouldSupportDriveSpecificIcons_(fileData) && fileData.metadata) { const {shared, isMachineRoot, isExternalMedia} = fileData.metadata; if (shared) { element.icon = constants.ICON_TYPES.SHARED_FOLDER; @@ -525,19 +521,18 @@ } /** - * Returns true if the entry supports the "shared" feature, as in, displays - * a shared icon. It's only supported inside "My Drive" or "Computers", even - * Shared Drive does not support it, the "My Drive" and "Computers" itself - * don't support it either, only their children. + * Returns true if fileData's entry supports the "shared" feature, as in, + * displays a shared icon. It's only supported inside "My Drive" or + * "Computers", even Shared Drive does not support it, the "My Drive" and + * "Computers" itself don't support it either, only their children. * - * Note: if the return value is true, the input entry is guaranteed to be + * Note: if the return value is true, fileData's entry is guaranteed to be * native Entry type. */ - private shouldSupportDriveSpecificIcons_(entry: Entry| - FilesAppEntry): entry is Entry { - return (this.isEntryInsideMyDrive_(entry) && !isVolumeEntry(entry)) || - (this.isEntryInsideComputers_(entry) && - !isGrandRootEntryInDrives(entry)); + private shouldSupportDriveSpecificIcons_(fileData: FileData): boolean { + return (isEntryInsideMyDrive(fileData) && !isVolumeEntry(fileData.entry)) || + (isEntryInsideComputers(fileData) && + !isGrandRootEntryInDrives(fileData.entry)); } /** @@ -583,7 +578,7 @@ })); // UMA: expand time. - const rootType = this.getRootType_(fileData.entry) ?? 'unknown'; + const rootType = fileData.rootType ?? 'unknown'; const metricName = `DirectoryTree.Expand.${rootType}`; this.recordUmaForItemExpandedOrCollapsed_(fileData); @@ -726,7 +721,7 @@ /** Record UMA for item expanded or collapsed. */ private recordUmaForItemExpandedOrCollapsed_(fileData: FileData) { - const rootType = this.getRootType_(fileData.entry) ?? 'unknown'; + const rootType = fileData.rootType ?? 'unknown'; const level = fileData.isRootEntry ? 'TopLevel' : 'NonTopLevel'; const metricName = `Location.OnEntryExpandedOrCollapsed.${level}`; metrics.recordEnum( @@ -735,7 +730,7 @@ /** Record UMA for tree item selected. */ private recordUmaForItemSelected_(fileData: FileData) { - const rootType = this.getRootType_(fileData.entry) ?? 'unknown'; + const rootType = fileData.rootType ?? 'unknown'; const level = fileData.isRootEntry ? 'TopLevel' : 'NonTopLevel'; const metricName = `Location.OnEntrySelected.${level}`; metrics.recordEnum( @@ -842,13 +837,18 @@ * is deleted. */ private updateTreeByEntry_(entry: DirectoryEntry) { + // TODO(b/271485133): Remove `getDirectory` call here and prevent + // convertEntryToFileData() below. entry.getDirectory( entry.fullPath, {create: false}, () => { + // Can't rely on store data to get entry's rootType, if the entry is + // grand root entry's first sub folder, the grand root entry might not + // be the in the store yet. + const fileData = convertEntryToFileData(entry); // If entry exists. // e.g. /a/b is deleted while watching /a. - if (this.isEntryInsideDrive_(entry) && - isGrandRootEntryInDrives(entry)) { + if (isEntryInsideDrive(fileData) && isGrandRootEntryInDrives(entry)) { // For grand root related changes, we need to re-read child // entries from the fake drive root level, because the grand root // might be show/hide based on if they have children or not. @@ -913,53 +913,4 @@ return false; } - - /** - * Gets the RootType of the Volume this entry belongs to. - */ - private getRootType_(entry: Entry|FilesAppEntry| - null): VolumeManagerCommon.RootType|null { - let rootType = null; - - if (entry) { - const locationInfo = this.volumeManager_.getLocationInfo(entry); - rootType = locationInfo ? locationInfo.rootType : null; - } - - return rootType; - } - - /** - * Returns true if the entry is inside any part of Drive 'My Drive'. - */ - private isEntryInsideMyDrive_(entry: Entry|FilesAppEntry|null): boolean { - const rootType = this.getRootType_(entry); - return !!rootType && rootType === VolumeManagerCommon.RootType.DRIVE; - } - - /** - * Returns true if the entry is inside any part of Drive 'Computers'. - */ - private isEntryInsideComputers_(entry: Entry|FilesAppEntry|null): boolean { - const rootType = this.getRootType_(entry); - return !!rootType && - (rootType === VolumeManagerCommon.RootType.COMPUTERS_GRAND_ROOT || - rootType === VolumeManagerCommon.RootType.COMPUTER); - } - - /** - * Returns true if the entry is inside any part of Drive. - */ - private isEntryInsideDrive_(entry: Entry|FilesAppEntry|null): boolean { - const rootType = this.getRootType_(entry); - return !!rootType && - (rootType === VolumeManagerCommon.RootType.DRIVE || - rootType === VolumeManagerCommon.RootType.SHARED_DRIVES_GRAND_ROOT || - rootType === VolumeManagerCommon.RootType.SHARED_DRIVE || - rootType === VolumeManagerCommon.RootType.COMPUTERS_GRAND_ROOT || - rootType === VolumeManagerCommon.RootType.COMPUTER || - rootType === VolumeManagerCommon.RootType.DRIVE_OFFLINE || - rootType === VolumeManagerCommon.RootType.DRIVE_SHARED_WITH_ME || - rootType === VolumeManagerCommon.RootType.DRIVE_FAKE_ROOT); - } }
diff --git a/ui/file_manager/file_manager/containers/directory_tree_container_unittest.ts b/ui/file_manager/file_manager/containers/directory_tree_container_unittest.ts index e45e4f9..d6789732 100644 --- a/ui/file_manager/file_manager/containers/directory_tree_container_unittest.ts +++ b/ui/file_manager/file_manager/containers/directory_tree_container_unittest.ts
@@ -21,7 +21,7 @@ import {createFakeVolumeMetadata, setUpFileManagerOnWindow, setupStore} from '../state/for_tests.js'; import {convertEntryToFileData} from '../state/reducers/all_entries.js'; import {convertVolumeInfoAndMetadataToVolume, driveRootEntryListKey} from '../state/reducers/volumes.js'; -import {getEmptyState, getEntry, getStore} from '../state/store.js'; +import {getEmptyState, getEntry, getFileData, getStore} from '../state/store.js'; import {XfTree} from '../widgets/xf_tree.js'; import {DirectoryTreeContainer} from './directory_tree_container.js'; @@ -716,14 +716,11 @@ // Add sub folders for them. myFilesFs.populate(['/folder1/']); driveFs.populate(['/root/folder1/']); - const driveRootEntryList = - getEntry(initialState, driveRootEntryListKey) as EntryList; + const driveRootEntryFileData = + getFileData(initialState, driveRootEntryListKey)!; // Disable the drive root entry. - driveRootEntryList.disabled = true; - const {volumeManager} = window.fileManager; - volumeManager.isDisabled = (volumeType) => { - return volumeType === VolumeManagerCommon.VolumeType.DRIVE; - }; + driveRootEntryFileData.disabled = true; + (driveRootEntryFileData.entry as EntryList).disabled = true; // Store initialization will notify DirectoryTree. setupStore(initialState); @@ -949,67 +946,6 @@ } /** - * Test private methods: isEntryInsideDrive_() and isEntryInsideMyDrive_(), - * which should return true when inside My Drive and any of its sub-directories; - * Should return false for everything else, including within Team Drive. - */ -export async function testInsideMyDriveAndInsideDrive(done: () => void) { - const initialState = getEmptyState(); - const directoryTree = directoryTreeContainer.tree; - - // Add MyFiles and Drive to the store. - const {myFilesFs, driveFs} = await addMyFilesAndDriveToStore(initialState); - // Add sub folders for them. - myFilesFs.populate(['/folder1/']); - driveFs.populate(['/root/folder1']); - // Store initialization will notify DirectoryTree. - const store = setupStore(initialState); - - // At top level, MyFiles and Drive should be listed. - await waitUntil(() => directoryTree.items.length === 2); - const myFilesItem = directoryTree.items[0]!; - const driveItem = directoryTree.items[1]!; - - await waitUntil(() => { - // Under the drive item, there exist 3 entries. In MyFiles should - // exist 1 entry folder1. - return driveItem.items.length === 3 && myFilesItem.items.length === 1; - }); - - // Use [] to access private methods to bypass Typescript check. - const isEntryInsideDrive = directoryTreeContainer['isEntryInsideDrive_'].bind( - directoryTreeContainer); - const isEntryInsideMyDrive = - directoryTreeContainer['isEntryInsideMyDrive_'].bind( - directoryTreeContainer); - - const driveRootEntryList = - getEntry(store.getState(), driveRootEntryListKey) as EntryList; - // insideMyDrive - assertFalse(isEntryInsideMyDrive(driveRootEntryList), 'Drive root'); - assertTrue(isEntryInsideMyDrive(driveFs.entries['/root']), 'My Drive root'); - assertFalse( - isEntryInsideMyDrive(driveFs.entries['/team_drives']), - 'Team Drives root'); - assertFalse( - isEntryInsideMyDrive(driveFs.entries['/Computers']), 'Offline root'); - assertFalse(isEntryInsideMyDrive(myFilesFs.entries['/']), 'MyFiles root'); - assertFalse( - isEntryInsideMyDrive(myFilesFs.entries['/folder1']), 'MyFiles folder1'); - // insideDrive - assertTrue(isEntryInsideDrive(driveRootEntryList), 'Drive root'); - assertTrue(isEntryInsideDrive(driveFs.entries['/root']), 'My Drive root'); - assertTrue( - isEntryInsideDrive(driveFs.entries['/team_drives']), 'Team Drives root'); - assertTrue(isEntryInsideDrive(driveFs.entries['/Computers']), 'Offline root'); - assertFalse(isEntryInsideDrive(myFilesFs.entries['/']), 'MyFiles root'); - assertFalse( - isEntryInsideDrive(myFilesFs.entries['/folder1']), 'MyFiles folder1'); - - done(); -} - -/** * Test adding FSPs. * Sub directories should be fetched for FSPs, but not for the Smb FSP. */
diff --git a/ui/file_manager/file_manager/externs/ts/state.js b/ui/file_manager/file_manager/externs/ts/state.js index f29f45b5..2916086 100644 --- a/ui/file_manager/file_manager/externs/ts/state.js +++ b/ui/file_manager/file_manager/externs/ts/state.js
@@ -45,6 +45,7 @@ * icon: (!string|!chrome.fileManagerPrivate.IconSet), * label: string, * volumeType: (VolumeManagerCommon.VolumeType|null), + * rootType: (VolumeManagerCommon.RootType|null), * metadata: !MetadataItem, * isDirectory: boolean, * type: !EntryType, @@ -53,6 +54,7 @@ * shouldDelayLoadingChildren: !boolean, * children: (!Array<!FileKey>), * expanded: !boolean, + * disabled: !boolean, * }} */ export let FileData;
diff --git a/ui/file_manager/file_manager/foreground/css/combobutton_gm3.css b/ui/file_manager/file_manager/foreground/css/combobutton_gm3.css new file mode 100644 index 0000000..7987319 --- /dev/null +++ b/ui/file_manager/file_manager/foreground/css/combobutton_gm3.css
@@ -0,0 +1,61 @@ +/* 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. */ + +.dialog-header button.combobutton { + align-items: stretch; + background: transparent; + border-radius: 3px; + cursor: pointer; + display: flex; + flex: none; + font-weight: 500; + outline: none; + padding: 8px; + position: relative; + user-select: none; +} + +.dialog-header .combobutton > .button { + align-items: center; + display: flex; +} + +.dialog-header .combobutton > .button > .action { + background-position: left center; + background-repeat: no-repeat; + background-size: 16px 16px; + padding-top: 1px; + z-index: 1; +} + +html[dir='rtl'] .dialog-header .combobutton > .button > .action { + background-position: right center; +} + +.dialog-header .combobutton > .button > .trigger { + align-items: center; + display: inline-flex; + height: 20px; + justify-content: center; + margin-inline-start: 8px; + width: 20px; +} + +/* This pseudo element expands clickable area of the .trigger */ +.dialog-header .combobutton > .button > .trigger::before { + content: ''; + display: block; + height: 48px; + position: absolute; + right: 12px; /* Same value as padding-inline-end of the button. */ + width: 20px; +} + +.dialog-header .combobutton:not([multiple]) > .button > .trigger { + display: none; +} + +.dialog-header .combobutton[hidden] { + display: none; +}
diff --git a/ui/file_manager/file_manager/foreground/css/file_manager_gm3.css b/ui/file_manager/file_manager/foreground/css/file_manager_gm3.css index 0471ac9f..ae1790d 100644 --- a/ui/file_manager/file_manager/foreground/css/file_manager_gm3.css +++ b/ui/file_manager/file_manager/foreground/css/file_manager_gm3.css
@@ -341,7 +341,7 @@ body.check-select .dialog-header { border-bottom: 1px solid var(--cros-separator-color); border-top: 1px solid transparent; - color: var(--cros-text-color-prominent); + color: var(--cros-sys-on_primary_container); } /* Display a border during check-select mode if we're in a dialog. (This @@ -351,31 +351,28 @@ } .dialog-header > .spacer { - flex: auto; + flex: 1 0 48px; } .dialog-header cr-button { - --active-bg: var(--cros-ripple-color); - --hover-bg-color: var(--cros-ripple-color); - --ink-color: var(--cros-ripple-color); + --hover-bg-color: var(--cros-sys-hover_on_subtle); + --ink-color: var(--cros-sys-ripple_neutral_on_subtle); --ripple-opacity: 100%; --text-color: currentColor; - border: 2px solid transparent; + border: none; border-radius: 18px; box-sizing: border-box; - color: var(--cros-text-color-primary); + color: var(--cros-sys-on_surface); cursor: pointer; height: 36px; - margin: 0 6px; - min-width: 36px; - padding: 0; + min-width: 48px; position: relative; text-transform: uppercase; z-index: 1; } html.pointer-active .dialog-header cr-button:not(:active):hover { - background-color: unset; + --hover-bg-color: none; cursor: default; } @@ -387,33 +384,16 @@ } .dialog-header cr-button[menu-shown] { - background: var(--cros-icon-button-pressed-color); + background-color: var(--cros-sys-primary_container); + color: var(--cros-sys-on_primary_container); } -.dialog-header cr-button:not([menu-shown]):not(:active):hover { - background-color: var(--cros-ripple-color); -} - -html.pointer-active .dialog-header cr-button:not([menu-shown]):not(:active):hover { - background-color: unset; - cursor: default; -} - -.dialog-header cr-button:active { - background: var(--cros-icon-button-pressed-color); +.dialog-header cr-button:not([aria-haspopup]):active { + background-color: var(--cros-sys-pressed_on_subtle); } html.focus-outline-visible .dialog-header cr-button:not(:active):focus { - border: 2px solid var(--cros-focus-ring-color); -} - -body.check-select button, -body.check-select button:hover { - color: var(--cros-text-color-primary); -} - -body.check-select .dialog-header button paper-ripple { - color: var(--cros-text-color-primary); + outline: 2px solid var(--cros-sys-focus_ring); } /** Avoid highlighting if element doesn't have focus by tab (tabindex=-1) @@ -425,7 +405,7 @@ body.check-select .dialog-header .menu-button:focus:not([tabindex='-1']):not(:active) { - background-color: var(--cros-ripple-color); + background-color: var(--cros-sys-ripple_neutral_on_subtle); } .dialog-header iron-icon, @@ -434,33 +414,25 @@ width: 16px; } -.dialog-header cr-button { - line-height: 32px; - padding: 0 8px; +.dialog-header cr-button.icon-button { + border-radius: 12px; + height: 40px; + margin: 4px; + min-width: 40px; + padding: 0; + width: 40px; } -.dialog-header button.icon-button { - -webkit-app-region: no-drag; - background-color: transparent; - background-image: none; - background-position: center; - background-repeat: no-repeat; - box-shadow: none; - outline: none; - position: relative; +.dialog-header cr-button.icon-button[aria-haspopup] { + --ink-color: var(--cros-sys-ripple_primary); } .dialog-header cr-button.icon-button > .icon { background-position: center; background-repeat: no-repeat; - height: 48px; - left: 0; - margin-inline-end: -8px; - margin-inline-start: -8px; - margin-top: -8px; + height: 20px; position: absolute; - top: 0; - width: 48px; + width: 20px; } .dialog-header cr-button.icon-button > xf-icon { @@ -468,9 +440,15 @@ } .dialog-header #tasks { - border-radius: 4px; - height: 32px; - padding: 0 12px; + color: var(--cros-sys-primary); + margin: 0 8px; + padding-bottom: 0; + padding-inline: 16px; + padding-top: 2px; +} + +.dialog-header #tasks[multiple] { + padding-inline-end: 12px; } .dialog-header cr-button > .icon, @@ -488,23 +466,27 @@ .dialog-header.files-ng #search-wrapper { align-items: center; - border-radius: 4px; - height: 40px; + border-radius: 8px; + height: 48px; transition: background-color 200ms ease; } .dialog-header.files-ng #search-wrapper.has-cursor, .dialog-header.files-ng #search-wrapper.has-text, .dialog-header.files-ng #search-wrapper.hide-pending { - background-color: var(--cros-textfield-background-color); - margin-inline-end: 16px; + background-color: var(--cros-sys-input_field_on_base); } .dialog-header #search-box cr-input { --cr-input-background-color: transparent; + --cr-input-border-bottom: transparent; --cr-input-border-radius: 0; + --cr-input-color: var(--cros-sys-on_surface); --cr-input-error-display: none; - --cr-input-padding-end: 20px; + --cr-input-focus-color: transparent; + --cr-input-min-height: 20px; + --cr-input-placeholder-color: var(--cros-sys-secondary); + --cr-input-padding-end: 0; --cr-input-padding-start: 0; display: inline-block; transition: width 200ms ease; @@ -585,7 +567,7 @@ } body.files-ng #files-selected-label { - margin-inline-start: 8px; + margin-inline: 8px 12px; } body.check-select #files-selected-label { @@ -593,7 +575,7 @@ } #cancel-selection-button { - --ink-color: var(--cros-ripple-color); + --ink-color: var(--cros-sys-ripple_neutral_on_subtle); border: none; box-shadow: none; color: currentColor; @@ -601,15 +583,16 @@ text-transform: none; } -/* TODO(adanilo) document the calc() reason. */ body.files-ng #cancel-selection-button { - border: 2px solid transparent; - margin-inline-start: calc(10px + 1px); - width: 36px; + height: 32px; + margin-inline-start: 12px; + min-width: 32px; + padding: 6px; + width: 32px; } html.focus-outline-visible body.files-ng #cancel-selection-button:focus:not([tabindex='-1']):not(:active) { - border: 2px solid var(--cros-icon-color-prominent); + outline: 2px solid var(--cros-sys-focus_ring); } body.files-ng #cancel-selection-button > span#cancel-selection-label { @@ -620,7 +603,7 @@ -webkit-mask-image: url(../images/files/ui/list_check.svg); -webkit-mask-position: center; -webkit-mask-repeat: no-repeat; - background-color: var(--cros-icon-color-prominent); + background-color: currentColor; flex: none; height: 20px; width: 20px; @@ -659,15 +642,9 @@ font-size: 14px; } -.dialog-header.files-ng #search-box cr-input { - --cr-input-border-bottom: transparent; - --cr-input-color: var(--cros-text-color-primary); - --cr-input-focus-color: transparent; - --cr-input-placeholder-color: var(--cros-text-color-secondary); -} - .dialog-header.files-ng #search-box cr-input::part(input) { - caret-color: var(--cr-input-color); + caret-color: var(--cros-sys-primary); + margin-inline-end: 16px; } body.check-select #search-box { @@ -699,8 +676,10 @@ .dialog-header.files-ng #search-box .clear { background: none; - height: 36px; - width: 36px; + height: 32px; + margin-inline-end: 4px; + padding: 0; + width: 32px; } html:not(.pointer-active) .dialog-header.files-ng #search-box .clear:hover { @@ -732,22 +711,21 @@ } .dialog-header.files-ng #pinned-toggle-label { - color: var(--cros-text-color-primary); - margin-inline-end: 16px; - margin-inline-start: 12px; + color: var(--cros-sys-on_surface); } .dialog-header.files-ng cr-toggle { - --cr-toggle-checked-bar-color: var(--cros-switch-track-color-active); + --cr-toggle-checked-bar-color: var(--cros-sys-primary); /* bar color above already defines the opacity, so use 100% here */ --cr-toggle-checked-bar-opacity: 100%; - --cr-toggle-checked-button-color: var(--cros-switch-knob-color-active); + --cr-toggle-checked-button-color: var(--cros-sys-on_primary); --cr-toggle-checked-ripple-color: var(--cros-focus-aura-color); - --cr-toggle-unchecked-bar-color: var(--cros-switch-track-color-inactive); - --cr-toggle-unchecked-button-color: var(--cros-switch-knob-color-inactive); + --cr-toggle-unchecked-bar-color: var(--cros-sys-secondary); + --cr-toggle-unchecked-button-color: var(--cros-sys-on_secondary); --cr-toggle-unchecked-ripple-color: var(--cros-ripple-color); --cr-toggle-box-shadow: var(--cros-elevation-1-shadow); --cr-toggle-ripple-diameter: 32px; + margin-inline: 6px; } /* only show the ripple ring for tab navigation */ @@ -1011,7 +989,7 @@ flex-direction: row; overflow: hidden; padding-bottom: 4px; - padding-inline: 8px 48px; + padding-inline-start: 8px; padding-top: 4px; } @@ -1022,14 +1000,13 @@ /* The toolbar indicator that means the current directory is read only. */ #read-only-indicator { align-items: center; - background-color: var(--cros-highlight-color-hover); - border-radius: 16px; + background-color: var(--cros-sys-disabled_container); + border: 1px solid var(--cros-sys-separator); + border-radius: 8px; display: flex; flex: none; height: 32px; - margin-inline-start: 12px; - padding: 0 16px; - padding-inline: 16px 12px; + padding-inline: 8px 12px; } body.check-select #read-only-indicator {
diff --git a/ui/file_manager/file_manager/foreground/elements/files_tooltip.html b/ui/file_manager/file_manager/foreground/elements/files_tooltip.html index a34a75c2..2496a17e 100644 --- a/ui/file_manager/file_manager/foreground/elements/files_tooltip.html +++ b/ui/file_manager/file_manager/foreground/elements/files_tooltip.html
@@ -39,6 +39,11 @@ padding: 12px 16px; } + :host-context([theme='refresh23']):host(.card-tooltip) { + background-color: var(--cros-sys-base_elevated); + color: var(--cros-sys-on_surface); + } + :host(.link-tooltip) { align-items: flex-start; flex-direction: column;
diff --git a/ui/file_manager/file_manager/state/reducers/all_entries.ts b/ui/file_manager/file_manager/state/reducers/all_entries.ts index 23cc7ea..242a1fff 100644 --- a/ui/file_manager/file_manager/state/reducers/all_entries.ts +++ b/ui/file_manager/file_manager/state/reducers/all_entries.ts
@@ -222,16 +222,19 @@ // getEntryLabel() can accept locationInfo=null, but TS doesn't recognize the // type definition in closure, hence the ! here. const label = util.getEntryLabel(locationInfo!, entry); - const volumeType = volumeInfo?.volumeType || null; + // For FakeEntry, we need to read from entry.volumeType because it doesn't + // have volumeInfo in the volume manager. + const volumeType = 'volumeType' in entry && entry.volumeType ? + entry.volumeType as VolumeManagerCommon.VolumeType : + (volumeInfo?.volumeType || null); const icon = getEntryIcon(entry, locationInfo, volumeType); /** * Update disabled attribute if entry supports disabled attribute and has a * non-null volumeType. */ - if ('disabled' in entry && 'volumeType' in entry && entry.volumeType) { - entry.disabled = volumeManager.isDisabled( - entry.volumeType as VolumeManagerCommon.VolumeType); + if ('disabled' in entry && volumeType) { + entry.disabled = volumeManager.isDisabled(volumeType); } const metadata = metadataModel ? @@ -245,8 +248,10 @@ isDirectory: entry.isDirectory, label, volumeType, + rootType: locationInfo?.rootType ?? null, metadata, expanded: false, + disabled: 'disabled' in entry ? entry.disabled as boolean : false, isRootEntry: !!locationInfo?.isRootEntry, // `isEjectable/shouldDelayLoadingChildren` is determined by its // corresponding volume, will be updated when volume is added.
diff --git a/ui/file_manager/file_manager/state/reducers/all_entries_unittest.ts b/ui/file_manager/file_manager/state/reducers/all_entries_unittest.ts index 794ef553..eeb8a60 100644 --- a/ui/file_manager/file_manager/state/reducers/all_entries_unittest.ts +++ b/ui/file_manager/file_manager/state/reducers/all_entries_unittest.ts
@@ -2,15 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import {assertEquals, assertFalse, assertNotEquals, assertTrue} from 'chrome://webui-test/chromeos/chai_assert.js'; +import {assertDeepEquals, assertEquals, assertFalse, assertNotEquals, assertTrue} from 'chrome://webui-test/chromeos/chai_assert.js'; import {MockVolumeManager} from '../../background/js/mock_volume_manager.js'; -import {EntryList, FakeEntryImpl, VolumeEntry} from '../../common/js/files_app_entry_types.js'; +import {EntryList, FakeEntryImpl, GuestOsPlaceholder, VolumeEntry} from '../../common/js/files_app_entry_types.js'; import {MockFileSystem} from '../../common/js/mock_entry.js'; import {waitUntil} from '../../common/js/test_error_reporting.js'; import {VolumeManagerCommon} from '../../common/js/volume_manager_types.js'; import {EntryType, FileData, State} from '../../externs/ts/state.js'; import {VolumeInfo} from '../../externs/volume_info.js'; +import {constants} from '../../foreground/js/constants.js'; import {MetadataItem} from '../../foreground/js/metadata/metadata_item.js'; import {MockMetadataModel} from '../../foreground/js/metadata/mock_metadata.js'; import {ActionType} from '../actions.js'; @@ -342,3 +343,140 @@ done(); } + +/** Tests converting VolumeEntry into FileData. */ +export async function testConvertVolumeEntryToFileData(done: () => void) { + const {volumeManager} = window.fileManager; + const downloadsVolumeInfo = volumeManager.getCurrentProfileVolumeInfo( + VolumeManagerCommon.VolumeType.DOWNLOADS)!; + const downloadsEntry = new VolumeEntry(downloadsVolumeInfo); + const got = convertEntryToFileData(downloadsEntry); + const want: FileData = { + entry: downloadsEntry, + icon: constants.ICON_TYPES.MY_FILES, + type: EntryType.VOLUME_ROOT, + isDirectory: true, + label: 'Downloads', + volumeType: VolumeManagerCommon.VolumeType.DOWNLOADS, + rootType: VolumeManagerCommon.RootType.DOWNLOADS, + metadata: {} as MetadataItem, + expanded: false, + disabled: false, + isRootEntry: true, + isEjectable: false, + shouldDelayLoadingChildren: false, + children: [], + }; + assertDeepEquals(want, got); + + // Now disabled the volume in the volume manager. + volumeManager.isDisabled = (volumeType) => + volumeType === VolumeManagerCommon.VolumeType.DOWNLOADS; + const fileData = convertEntryToFileData(downloadsEntry); + assertEquals(true, fileData.disabled); + + done(); +} + +/** Tests converting EntryList into FileData. */ +export async function testConvertEntryListToFileData(done: () => void) { + const myFilesEntryList = + new EntryList('My files', VolumeManagerCommon.RootType.MY_FILES); + const got = convertEntryToFileData(myFilesEntryList); + const want: FileData = { + entry: myFilesEntryList, + icon: constants.ICON_TYPES.MY_FILES, + type: EntryType.ENTRY_LIST, + isDirectory: true, + label: 'My files', + volumeType: VolumeManagerCommon.VolumeType.DOWNLOADS, + rootType: VolumeManagerCommon.VolumeType.MY_FILES, + metadata: {} as MetadataItem, + expanded: false, + disabled: false, + isRootEntry: true, + isEjectable: false, + shouldDelayLoadingChildren: false, + children: [], + }; + assertDeepEquals(want, got); + + done(); +} + +/** Tests converting FakeEntry into FileData. */ +export async function testConvertFakeEntryToFileData(done: () => void) { + const androidFakeEntry = new GuestOsPlaceholder( + 'Android files', 0, chrome.fileManagerPrivate.VmType.ARCVM); + const got = convertEntryToFileData(androidFakeEntry); + const want: FileData = { + entry: androidFakeEntry, + icon: constants.ICON_TYPES.ANDROID_FILES, + type: EntryType.PLACEHOLDER, + isDirectory: true, + label: 'Android files', + volumeType: VolumeManagerCommon.VolumeType.ANDROID_FILES, + rootType: VolumeManagerCommon.RootType.GUEST_OS, + metadata: {} as MetadataItem, + expanded: false, + disabled: false, + isRootEntry: true, + isEjectable: false, + shouldDelayLoadingChildren: false, + children: [], + }; + assertDeepEquals(want, got); + + done(); +} + +/** Tests converting native file entry into FileData. */ +export async function testConvertNativeFileEntryToFileData(done: () => void) { + const fileEntry = fileSystem.entries['/dir-2/file-1.txt']; + const got = convertEntryToFileData(fileEntry); + const want: FileData = { + entry: fileEntry, + icon: 'text', + type: EntryType.FS_API, + isDirectory: false, + label: 'file-1.txt', + volumeType: VolumeManagerCommon.VolumeType.DOWNLOADS, + rootType: VolumeManagerCommon.RootType.DOWNLOADS, + metadata: {} as MetadataItem, + expanded: false, + disabled: false, + isRootEntry: false, + isEjectable: false, + shouldDelayLoadingChildren: false, + children: [], + }; + assertDeepEquals(want, got); + + done(); +} + +/** Tests converting native directory entry into FileData. */ +export async function testConvertNativeDirectoryEntryToFileData( + done: () => void) { + const directoryEntry = fileSystem.entries['/dir-1']; + const got = convertEntryToFileData(directoryEntry); + const want: FileData = { + entry: directoryEntry, + icon: constants.ICON_TYPES.FOLDER, + type: EntryType.FS_API, + isDirectory: true, + label: 'dir-1', + volumeType: VolumeManagerCommon.VolumeType.DOWNLOADS, + rootType: VolumeManagerCommon.RootType.DOWNLOADS, + metadata: {} as MetadataItem, + expanded: false, + disabled: false, + isRootEntry: false, + isEjectable: false, + shouldDelayLoadingChildren: false, + children: [], + }; + assertDeepEquals(want, got); + + done(); +}
diff --git a/ui/file_manager/file_manager/widgets/xf_breadcrumb.ts b/ui/file_manager/file_manager/widgets/xf_breadcrumb.ts index c918703..ca1145f9 100644 --- a/ui/file_manager/file_manager/widgets/xf_breadcrumb.ts +++ b/ui/file_manager/file_manager/widgets/xf_breadcrumb.ts
@@ -589,7 +589,7 @@ } cr-action-menu { - --cr-menu-background-color: var(--cros-sys-app_base_elevated); + --cr-menu-background-color: var(--cros-sys-base_elevated); --cr-menu-background-sheen: none; /* TODO(wenbojie): use elevation variable when it's ready. --cros-sys-elevation3 */
diff --git a/ui/file_manager/file_manager/widgets/xf_select.ts b/ui/file_manager/file_manager/widgets/xf_select.ts index 39eed9e..cb26cbf2 100644 --- a/ui/file_manager/file_manager/widgets/xf_select.ts +++ b/ui/file_manager/file_manager/widgets/xf_select.ts
@@ -11,7 +11,7 @@ import {AnchorAlignment, CrActionMenuElement} from 'chrome://resources/cr_elements/cr_action_menu/cr_action_menu.js'; import {CrButtonElement} from 'chrome://resources/cr_elements/cr_button/cr_button.js'; -import {css, CSSResultGroup, customElement, html, property, query, state, XfBase} from './xf_base.js'; +import {css, CSSResultGroup, customElement, html, property, query, XfBase} from './xf_base.js'; /** * The data structure used to set the new options on the select element. @@ -102,11 +102,6 @@ @query('cr-action-menu') private $optionsMenu_?: CrActionMenuElement; /** - * Keeps track of whether we are showing the options menu. - */ - @state() private optionsVisible_: boolean = false; - - /** * The currently selected option. */ private selectedOption_: XfSelectedValue = { @@ -138,7 +133,7 @@ * collapsed. */ get expanded(): boolean { - return this.optionsVisible_; + return this.$optionsMenu_ ? this.$optionsMenu_.open : false; } /** @@ -157,7 +152,7 @@ return html` <cr-button id="dropdown-toggle" aria-haspopup="menu" - aria-expanded=${this.optionsVisible_} + aria-expanded=${this.expanded} @click=${this.onToggleOptions_}> ${iconPart}${labelPart}<span id="dropdown-icon"></span> </cr-button>`; @@ -248,7 +243,7 @@ * dropdown options. */ private onToggleOptions_(): void { - if (this.optionsVisible_) { + if (this.expanded) { this.closeOptions_(); } else { this.openOptions_(); @@ -259,12 +254,11 @@ * Opens the dropdown options, providing they were closed. */ private openOptions_() { - if (!this.optionsVisible_) { + if (!this.expanded) { const element: HTMLElement = this.$toggleDropdownButton_!; const top = element.offsetTop + element.offsetHeight + 8; this.$optionsMenu_!.showAt( element, {top: top, anchorAlignmentX: AnchorAlignment.AFTER_START}); - this.optionsVisible_ = true; } } @@ -272,9 +266,8 @@ * Closes the dropdown options, providing they were open. */ private closeOptions_() { - if (this.optionsVisible_) { + if (this.expanded) { this.$optionsMenu_!.close(); - this.optionsVisible_ = false; } }
diff --git a/ui/file_manager/file_names.gni b/ui/file_manager/file_names.gni index bc72c979..1ce64f31 100644 --- a/ui/file_manager/file_names.gni +++ b/ui/file_manager/file_names.gni
@@ -322,6 +322,9 @@ ] ts_test_files = [ + # Common. + "file_manager/common/js/entry_utils_unittest.ts", + # Containers "file_manager/containers/breadcrumb_container_unittest.ts", "file_manager/containers/directory_tree_container_unittest.ts",
diff --git a/ui/gfx/android/android_surface_control_compat.cc b/ui/gfx/android/android_surface_control_compat.cc index 243541a..b94d128 100644 --- a/ui/gfx/android/android_surface_control_compat.cc +++ b/ui/gfx/android/android_surface_control_compat.cc
@@ -10,13 +10,17 @@ #include "base/android/build_info.h" #include "base/atomic_sequence_num.h" +#include "base/containers/flat_set.h" #include "base/debug/crash_logging.h" +#include "base/debug/dump_without_crashing.h" #include "base/functional/bind.h" #include "base/hash/md5_constexpr.h" #include "base/logging.h" #include "base/memory/ptr_util.h" #include "base/memory/raw_ptr.h" +#include "base/no_destructor.h" #include "base/strings/string_number_conversions.h" +#include "base/synchronization/lock.h" #include "base/system/sys_info.h" #include "base/task/bind_post_task.h" #include "base/task/single_thread_task_runner.h" @@ -481,6 +485,16 @@ return kMask ^ transaction_id; } +base::Lock& GetGlobalLock() { + static base::NoDestructor<base::Lock> lock; + return *lock; +} + +base::flat_set<int>& GetGlobalPendingCompleteCallbackIds() { + static base::NoDestructor<base::flat_set<int>> set; + return *set; +} + // Note that the framework API states that this callback can be dispatched on // any thread (in practice it should be a binder thread). void OnTransactionCompletedOnAnyThread(void* context, @@ -493,6 +507,17 @@ "toplevel.flow", "gfx::SurfaceControlTransaction completed", GetTraceIdForTransaction(ack_ctx->id), TRACE_EVENT_FLAG_FLOW_IN); + bool dump = false; + { + base::AutoLock lock(GetGlobalLock()); + size_t num_removed = + GetGlobalPendingCompleteCallbackIds().erase(ack_ctx->id); + dump = !num_removed; + } + if (dump) { + base::debug::DumpWithoutCrashing(base::Location::Current(), base::Days(1)); + } + std::move(ack_ctx->callback).Run(std::move(transaction_stats)); delete ack_ctx; } @@ -883,6 +908,16 @@ ack_ctx->callback = std::move(on_complete_cb_); ack_ctx->id = id_; + bool dump = false; + { + base::AutoLock lock(GetGlobalLock()); + auto result = GetGlobalPendingCompleteCallbackIds().insert(id_); + dump = !result.second; + } + if (dump) { + base::debug::DumpWithoutCrashing(base::Location::Current(), + base::Days(1)); + } SurfaceControlMethods::Get().ASurfaceTransaction_setOnCompleteFn( transaction_, ack_ctx, &OnTransactionCompletedOnAnyThread); need_to_apply_ = true;
diff --git a/ui/views/controls/button/button.cc b/ui/views/controls/button/button.cc index 61c61384..5513499 100644 --- a/ui/views/controls/button/button.cc +++ b/ui/views/controls/button/button.cc
@@ -540,8 +540,6 @@ } if (GetEnabled()) node_data->SetDefaultActionVerb(ax::mojom::DefaultActionVerb::kPress); - - button_controller_->UpdateAccessibleNodeData(node_data); } void Button::VisibilityChanged(View* starting_from, bool visible) {
diff --git a/ui/views/controls/button/button_controller.cc b/ui/views/controls/button/button_controller.cc index b27d698..404033706 100644 --- a/ui/views/controls/button/button_controller.cc +++ b/ui/views/controls/button/button_controller.cc
@@ -137,8 +137,6 @@ } } -void ButtonController::UpdateAccessibleNodeData(ui::AXNodeData* node_data) {} - bool ButtonController::IsTriggerableEvent(const ui::Event& event) { return event.type() == ui::ET_GESTURE_TAP_DOWN || event.type() == ui::ET_GESTURE_TAP ||
diff --git a/ui/views/controls/button/button_controller.h b/ui/views/controls/button/button_controller.h index 941586c6..3f3cb40 100644 --- a/ui/views/controls/button/button_controller.h +++ b/ui/views/controls/button/button_controller.h
@@ -50,9 +50,6 @@ virtual bool OnKeyReleased(const ui::KeyEvent& event); virtual void OnGestureEvent(ui::GestureEvent* event); - // Updates |node_data| for a button based on the functionality. - virtual void UpdateAccessibleNodeData(ui::AXNodeData* node_data); - // Methods that parallel respective methods in Button: virtual bool IsTriggerableEvent(const ui::Event& event);
diff --git a/ui/views/controls/button/checkbox.cc b/ui/views/controls/button/checkbox.cc index fb0b27ab..da305daa1 100644 --- a/ui/views/controls/button/checkbox.cc +++ b/ui/views/controls/button/checkbox.cc
@@ -91,6 +91,8 @@ // Avoid the default ink-drop mask to allow the ripple effect to extend beyond // the checkbox view (otherwise it gets clipped which looks weird). views::InstallEmptyHighlightPathGenerator(this); + + SetAccessibilityProperties(ax::mojom::Role::kCheckBox); } Checkbox::~Checkbox() = default; @@ -146,7 +148,6 @@ void Checkbox::GetAccessibleNodeData(ui::AXNodeData* node_data) { LabelButton::GetAccessibleNodeData(node_data); - node_data->role = ax::mojom::Role::kCheckBox; const ax::mojom::CheckedState checked_state = GetChecked() ? ax::mojom::CheckedState::kTrue : ax::mojom::CheckedState::kFalse;
diff --git a/ui/views/controls/button/checkbox_unittest.cc b/ui/views/controls/button/checkbox_unittest.cc index 69ac9fd9..f041472 100644 --- a/ui/views/controls/button/checkbox_unittest.cc +++ b/ui/views/controls/button/checkbox_unittest.cc
@@ -61,10 +61,24 @@ ui::AXNodeData ax_data; checkbox()->GetAccessibleNodeData(&ax_data); - EXPECT_EQ(ax_data.GetString16Attribute(ax::mojom::StringAttribute::kName), label_text); + EXPECT_EQ(checkbox()->GetAccessibleName(), label_text); EXPECT_EQ(ax_data.role, ax::mojom::Role::kCheckBox); + EXPECT_EQ(checkbox()->GetAccessibleRole(), ax::mojom::Role::kCheckBox); + EXPECT_EQ(ax_data.GetCheckedState(), ax::mojom::CheckedState::kFalse); + + ax_data = ui::AXNodeData(); + checkbox()->SetChecked(true); + checkbox()->GetAccessibleNodeData(&ax_data); + EXPECT_EQ(ax_data.GetCheckedState(), ax::mojom::CheckedState::kTrue); + + ax_data = ui::AXNodeData(); + checkbox()->SetAccessibleRole(ax::mojom::Role::kMenuItemCheckBox); + checkbox()->GetAccessibleNodeData(&ax_data); + EXPECT_EQ(ax_data.role, ax::mojom::Role::kMenuItemCheckBox); + EXPECT_EQ(checkbox()->GetAccessibleRole(), + ax::mojom::Role::kMenuItemCheckBox); } } // namespace views
diff --git a/ui/views/controls/button/image_button.cc b/ui/views/controls/button/image_button.cc index 32de82bb..c0255128 100644 --- a/ui/views/controls/button/image_button.cc +++ b/ui/views/controls/button/image_button.cc
@@ -6,6 +6,7 @@ #include <utility> +#include "base/functional/bind.h" #include "base/strings/utf_string_conversions.h" #include "base/trace_event/trace_event.h" #include "ui/accessibility/ax_enums.mojom.h" @@ -15,7 +16,10 @@ #include "ui/gfx/canvas.h" #include "ui/gfx/image/image_skia_operations.h" #include "ui/gfx/scoped_canvas.h" +#include "ui/views/animation/ink_drop.h" #include "ui/views/background.h" +#include "ui/views/controls/highlight_path_generator.h" +#include "ui/views/layout/layout_provider.h" #include "ui/views/painter.h" #include "ui/views/widget/widget.h" @@ -149,6 +153,67 @@ SchedulePaint(); } +// static +std::unique_ptr<ImageButton> ImageButton::CreateIconButton( + PressedCallback callback, + const gfx::VectorIcon& icon, + const std::u16string& accessible_name, + MaterialIconStyle icon_style) { + const int kSmallIconSize = 14; + const int kLargeIconSize = 20; + int icon_size = (icon_style == MaterialIconStyle::kLarge) ? kLargeIconSize + : kSmallIconSize; + // Icon images have padding between the image and image border. To account + // for that padding, add a general padding value. This value might be + // incorrect depending on the icon image. + icon_size += + LayoutProvider::Get()->GetDistanceMetric(DISTANCE_VECTOR_ICON_PADDING); + + std::unique_ptr<ImageButton> icon_button = + std::make_unique<ImageButton>(callback); + icon_button->SetImageModel( + ButtonState::STATE_NORMAL, + ui::ImageModel::FromVectorIcon(icon, ui::kColorIcon, icon_size)); + icon_button->SetImageModel( + ButtonState::STATE_HOVERED, + ui::ImageModel::FromVectorIcon(icon, ui::kColorIcon, icon_size)); + icon_button->SetImageModel( + ButtonState::STATE_PRESSED, + ui::ImageModel::FromVectorIcon(icon, ui::kColorIcon, icon_size)); + icon_button->SetImageModel( + ButtonState::STATE_DISABLED, + ui::ImageModel::FromVectorIcon(icon, ui::kColorIconDisabled, icon_size)); + + const gfx::Insets target_insets = + LayoutProvider::Get()->GetInsetsMetric(InsetsMetric::INSETS_ICON_BUTTON); + icon_button->SetBorder(views::CreateEmptyBorder(target_insets)); + + const int kSmallIconButtonSize = 24; + const int kLargeIconButtonSize = 32; + int button_size = (icon_style == MaterialIconStyle::kLarge) + ? kLargeIconButtonSize + : kSmallIconButtonSize; + const int highlight_radius = LayoutProvider::Get()->GetCornerRadiusMetric( + views::Emphasis::kMaximum, gfx::Size(button_size, button_size)); + views::InstallRoundRectHighlightPathGenerator( + icon_button.get(), gfx::Insets(), highlight_radius); + + InkDrop::Get(icon_button.get())->SetMode(views::InkDropHost::InkDropMode::ON); + icon_button->SetHasInkDropActionOnClick(true); + icon_button->SetShowInkDropWhenHotTracked(true); + InkDrop::Get(icon_button.get()) + ->SetBaseColorCallback(base::BindRepeating( + [](ImageButton* host) { + return host->GetColorProvider()->GetColor( + ui::kColorSysOnSurfaceSubtle); + }, + icon_button.get())); + + icon_button->SetAccessibleName(accessible_name); + + return icon_button; +} + void ImageButton::PaintButtonContents(gfx::Canvas* canvas) { // TODO(estade|tdanderson|bruthig): The ink drop layer should be positioned // behind the button's image which means the image needs to be painted to its @@ -227,6 +292,7 @@ ToggleImageButton::ToggleImageButton(PressedCallback callback) : ImageButton(std::move(callback)) { + SetAccessibilityProperties(ax::mojom::Role::kToggleButton); } ToggleImageButton::~ToggleImageButton() = default; @@ -277,6 +343,11 @@ void ToggleImageButton::SetToggledTooltipText(const std::u16string& tooltip) { if (tooltip == toggled_tooltip_text_) return; + + if (toggled_accessible_name_.empty() && !tooltip.empty()) { + SetAccessibleName(tooltip); + } + toggled_tooltip_text_ = tooltip; OnPropertyChanged(&toggled_tooltip_text_, kPropertyEffectsNone); } @@ -288,7 +359,14 @@ void ToggleImageButton::SetToggledAccessibleName(const std::u16string& name) { if (name == toggled_accessible_name_) return; + toggled_accessible_name_ = name; + if (!toggled_accessible_name_.empty()) { + SetAccessibleName(toggled_accessible_name_); + } else if (!toggled_tooltip_text_.empty()) { + SetAccessibleName(toggled_tooltip_text_); + } + OnPropertyChanged(&toggled_accessible_name_, kPropertyEffectsNone); } @@ -336,11 +414,6 @@ if (!toggled_) return; - if (!toggled_accessible_name_.empty()) - node_data->SetName(toggled_accessible_name_); - else if (!toggled_tooltip_text_.empty()) - node_data->SetName(toggled_tooltip_text_); - // Use the visual pressed image as a cue for making this control into an // accessible toggle button. if ((toggled_ && !images_[ButtonState::STATE_NORMAL].IsEmpty()) ||
diff --git a/ui/views/controls/button/image_button.h b/ui/views/controls/button/image_button.h index a3aea3f..8da5ac8d 100644 --- a/ui/views/controls/button/image_button.h +++ b/ui/views/controls/button/image_button.h
@@ -75,6 +75,16 @@ views::PaintInfo::ScaleType GetPaintScaleType() const override; void OnThemeChanged() override; + enum class MaterialIconStyle { kSmall, kLarge }; + + // Static method to create a Icon button with Google Material style + // guidelines. + static std::unique_ptr<ImageButton> CreateIconButton( + PressedCallback callback, + const gfx::VectorIcon& icon, + const std::u16string& accessible_name, + MaterialIconStyle icon_style = MaterialIconStyle::kLarge); + protected: // Overridden from Button: void PaintButtonContents(gfx::Canvas* canvas) override; @@ -166,10 +176,17 @@ Background* GetToggledBackground() const { return toggled_background_.get(); } // Get/Set the tooltip text displayed when the button is toggled. + // TODO(accessibility): This seems like it provides a fallback name. + // Should callers who want this to be the name use `SetAccessibleName`? + // If it should be a description, then `SetAccessibleDescription`? + // Note that if something lacks an accessible description but has a tooltip, + // the tooltip text will be used. Does the tooltip text match this text? std::u16string GetToggledTooltipText() const; void SetToggledTooltipText(const std::u16string& tooltip); // Get/Set the accessible text used when the button is toggled. + // TODO(accessibility): Can we just use the `AccessibleName` getter/setter + // from View? std::u16string GetToggledAccessibleName() const; void SetToggledAccessibleName(const std::u16string& name);
diff --git a/ui/views/controls/button/image_button_unittest.cc b/ui/views/controls/button/image_button_unittest.cc index 5c6567bd..f7611802 100644 --- a/ui/views/controls/button/image_button_unittest.cc +++ b/ui/views/controls/button/image_button_unittest.cc
@@ -197,4 +197,34 @@ EXPECT_EQ(1, parent.pref_size_changed_calls()); } +TEST_F(ImageButtonTest, ImageButtonAccessibleProperties) { + ImageButton button; + ui::AXNodeData data; + button.GetAccessibleNodeData(&data); + EXPECT_EQ(data.role, ax::mojom::Role::kButton); + EXPECT_EQ(button.GetAccessibleRole(), ax::mojom::Role::kButton); + + button.SetAccessibleRole(ax::mojom::Role::kPopUpButton); + + data = ui::AXNodeData(); + button.GetAccessibleNodeData(&data); + EXPECT_EQ(data.role, ax::mojom::Role::kPopUpButton); + EXPECT_EQ(button.GetAccessibleRole(), ax::mojom::Role::kPopUpButton); +} + +TEST_F(ImageButtonTest, ToggleImageButtonAccessibleProperties) { + ToggleImageButton button; + ui::AXNodeData data; + button.GetAccessibleNodeData(&data); + EXPECT_EQ(data.role, ax::mojom::Role::kToggleButton); + EXPECT_EQ(button.GetAccessibleRole(), ax::mojom::Role::kToggleButton); + + button.SetAccessibleRole(ax::mojom::Role::kPopUpButton); + + data = ui::AXNodeData(); + button.GetAccessibleNodeData(&data); + EXPECT_EQ(data.role, ax::mojom::Role::kPopUpButton); + EXPECT_EQ(button.GetAccessibleRole(), ax::mojom::Role::kPopUpButton); +} + } // namespace views
diff --git a/ui/views/controls/button/menu_button.cc b/ui/views/controls/button/menu_button.cc index b1d3b76..3203d471 100644 --- a/ui/views/controls/button/menu_button.cc +++ b/ui/views/controls/button/menu_button.cc
@@ -7,6 +7,7 @@ #include <memory> #include <utility> +#include "ui/accessibility/ax_enums.mojom.h" #include "ui/base/metadata/metadata_impl_macros.h" #include "ui/events/event.h" #include "ui/views/controls/button/button_controller_delegate.h" @@ -28,6 +29,7 @@ SetButtonController(std::move(menu_button_controller)); SetFocusBehavior(FocusBehavior::ACCESSIBLE_ONLY); + SetAccessibilityProperties(ax::mojom::Role::kPopUpButton); } MenuButton::~MenuButton() = default; @@ -40,6 +42,14 @@ menu_button_controller_->SetCallback(std::move(callback)); } +void MenuButton::GetAccessibleNodeData(ui::AXNodeData* node_data) { + LabelButton::GetAccessibleNodeData(node_data); + node_data->SetHasPopup(ax::mojom::HasPopup::kMenu); + if (GetEnabled()) { + node_data->SetDefaultActionVerb(ax::mojom::DefaultActionVerb::kOpen); + } +} + void MenuButton::NotifyClick(const ui::Event& event) { // Run pressed callback via MenuButtonController, instead of directly. button_controller()->Activate(&event);
diff --git a/ui/views/controls/button/menu_button.h b/ui/views/controls/button/menu_button.h index db8131d..53ba2b8 100644 --- a/ui/views/controls/button/menu_button.h +++ b/ui/views/controls/button/menu_button.h
@@ -41,6 +41,7 @@ // Button: void SetCallback(PressedCallback callback) override; + void GetAccessibleNodeData(ui::AXNodeData* node_data) override; protected: // Button:
diff --git a/ui/views/controls/button/menu_button_controller.cc b/ui/views/controls/button/menu_button_controller.cc index 4eb3e629..4a2dd57 100644 --- a/ui/views/controls/button/menu_button_controller.cc +++ b/ui/views/controls/button/menu_button_controller.cc
@@ -8,7 +8,6 @@ #include "base/functional/bind.h" #include "ui/accessibility/ax_enums.mojom.h" -#include "ui/accessibility/ax_node_data.h" #include "ui/base/dragdrop/drag_drop_types.h" #include "ui/base/interaction/element_identifier.h" #include "ui/events/event_constants.h" @@ -177,13 +176,6 @@ return false; } -void MenuButtonController::UpdateAccessibleNodeData(ui::AXNodeData* node_data) { - node_data->role = ax::mojom::Role::kPopUpButton; - node_data->SetHasPopup(ax::mojom::HasPopup::kMenu); - if (button()->GetEnabled()) - node_data->SetDefaultActionVerb(ax::mojom::DefaultActionVerb::kOpen); -} - bool MenuButtonController::IsTriggerableEvent(const ui::Event& event) { return ButtonController::IsTriggerableEvent(event) && IsTriggerableEventType(event) && is_intentional_menu_trigger_;
diff --git a/ui/views/controls/button/menu_button_controller.h b/ui/views/controls/button/menu_button_controller.h index 7f23dbe7..358e420 100644 --- a/ui/views/controls/button/menu_button_controller.h +++ b/ui/views/controls/button/menu_button_controller.h
@@ -59,7 +59,6 @@ bool OnKeyPressed(const ui::KeyEvent& event) override; bool OnKeyReleased(const ui::KeyEvent& event) override; void OnGestureEvent(ui::GestureEvent* event) override; - void UpdateAccessibleNodeData(ui::AXNodeData* node_data) override; bool IsTriggerableEvent(const ui::Event& event) override; // Calls TakeLock with is_sibling_menu_show as false and a nullptr to the
diff --git a/ui/views/controls/button/menu_button_unittest.cc b/ui/views/controls/button/menu_button_unittest.cc index 2c4ff9e2..b8c6ace 100644 --- a/ui/views/controls/button/menu_button_unittest.cc +++ b/ui/views/controls/button/menu_button_unittest.cc
@@ -610,4 +610,23 @@ button()->OnGestureEvent(&gesture_event); } +TEST_F(MenuButtonTest, AccessibleProperties) { + ConfigureMenuButton(std::make_unique<TestMenuButton>()); + ui::AXNodeData data; + button()->GetAccessibleNodeData(&data); + EXPECT_EQ(button()->GetAccessibleRole(), ax::mojom::Role::kPopUpButton); + EXPECT_EQ(data.role, ax::mojom::Role::kPopUpButton); + EXPECT_EQ(data.GetHasPopup(), ax::mojom::HasPopup::kMenu); + EXPECT_EQ(data.GetDefaultActionVerb(), ax::mojom::DefaultActionVerb::kOpen); + + button()->SetAccessibleRole(ax::mojom::Role::kButton); + + data = ui::AXNodeData(); + button()->GetAccessibleNodeData(&data); + EXPECT_EQ(button()->GetAccessibleRole(), ax::mojom::Role::kButton); + EXPECT_EQ(data.role, ax::mojom::Role::kButton); + EXPECT_EQ(data.GetHasPopup(), ax::mojom::HasPopup::kMenu); + EXPECT_EQ(data.GetDefaultActionVerb(), ax::mojom::DefaultActionVerb::kOpen); +} + } // namespace views
diff --git a/ui/views/controls/button/radio_button.cc b/ui/views/controls/button/radio_button.cc index 4b61c2d1..18665f0 100644 --- a/ui/views/controls/button/radio_button.cc +++ b/ui/views/controls/button/radio_button.cc
@@ -9,7 +9,6 @@ #include "base/ranges/algorithm.h" #include "ui/accessibility/ax_action_data.h" #include "ui/accessibility/ax_enums.mojom.h" -#include "ui/accessibility/ax_node_data.h" #include "ui/base/metadata/metadata_impl_macros.h" #include "ui/base/resource/resource_bundle.h" #include "ui/events/event_utils.h" @@ -33,15 +32,11 @@ RadioButton::RadioButton(const std::u16string& label, int group_id) : Checkbox(label) { SetGroup(group_id); + SetAccessibilityProperties(ax::mojom::Role::kRadioButton); } RadioButton::~RadioButton() = default; -void RadioButton::GetAccessibleNodeData(ui::AXNodeData* node_data) { - Checkbox::GetAccessibleNodeData(node_data); - node_data->role = ax::mojom::Role::kRadioButton; -} - View* RadioButton::GetSelectedViewForGroup(int group) { Views views; GetViewsInGroupFromParent(group, &views);
diff --git a/ui/views/controls/button/radio_button.h b/ui/views/controls/button/radio_button.h index d4114df..7a4a53e 100644 --- a/ui/views/controls/button/radio_button.h +++ b/ui/views/controls/button/radio_button.h
@@ -28,7 +28,6 @@ ~RadioButton() override; // Overridden from View: - void GetAccessibleNodeData(ui::AXNodeData* node_data) override; View* GetSelectedViewForGroup(int group) override; bool HandleAccessibleAction(const ui::AXActionData& action_data) override; bool IsGroupFocusTraversable() const override;
diff --git a/ui/views/controls/button/radio_button_unittest.cc b/ui/views/controls/button/radio_button_unittest.cc index 20a45fb..f9cf8ad5 100644 --- a/ui/views/controls/button/radio_button_unittest.cc +++ b/ui/views/controls/button/radio_button_unittest.cc
@@ -131,4 +131,28 @@ EXPECT_EQ(button1, focus_manager->GetFocusedView()); } +TEST_F(RadioButtonTest, AccessibilityTest) { + RadioButton* button = new RadioButton(u"Item 1", kGroup); + ui::AXNodeData data; + button->GetAccessibleNodeData(&data); + + EXPECT_EQ(data.GetString16Attribute(ax::mojom::StringAttribute::kName), + u"Item 1"); + EXPECT_EQ(button->GetAccessibleName(), u"Item 1"); + EXPECT_EQ(data.role, ax::mojom::Role::kRadioButton); + EXPECT_EQ(button->GetAccessibleRole(), ax::mojom::Role::kRadioButton); + EXPECT_EQ(data.GetCheckedState(), ax::mojom::CheckedState::kFalse); + + data = ui::AXNodeData(); + button->SetChecked(true); + button->GetAccessibleNodeData(&data); + EXPECT_EQ(data.GetCheckedState(), ax::mojom::CheckedState::kTrue); + + data = ui::AXNodeData(); + button->SetAccessibleRole(ax::mojom::Role::kMenuItemRadio); + button->GetAccessibleNodeData(&data); + EXPECT_EQ(data.role, ax::mojom::Role::kMenuItemRadio); + EXPECT_EQ(button->GetAccessibleRole(), ax::mojom::Role::kMenuItemRadio); +} + } // namespace views
diff --git a/ui/views/controls/button/toggle_button.cc b/ui/views/controls/button/toggle_button.cc index d1056314..ca0a0e9 100644 --- a/ui/views/controls/button/toggle_button.cc +++ b/ui/views/controls/button/toggle_button.cc
@@ -292,6 +292,8 @@ SetInstallFocusRingOnFocus(true); FocusRing::Get(this)->SetPathGenerator( std::make_unique<FocusRingHighlightPathGenerator>()); + + SetAccessibilityProperties(ax::mojom::Role::kSwitch); } ToggleButton::~ToggleButton() { @@ -522,8 +524,6 @@ void ToggleButton::GetAccessibleNodeData(ui::AXNodeData* node_data) { Button::GetAccessibleNodeData(node_data); - - node_data->role = ax::mojom::Role::kSwitch; node_data->SetCheckedState(GetIsOn() ? ax::mojom::CheckedState::kTrue : ax::mojom::CheckedState::kFalse); }
diff --git a/ui/views/controls/button/toggle_button.h b/ui/views/controls/button/toggle_button.h index 04f4d6e..1d719fe 100644 --- a/ui/views/controls/button/toggle_button.h +++ b/ui/views/controls/button/toggle_button.h
@@ -81,6 +81,7 @@ friend class TestToggleButton; class FocusRingHighlightPathGenerator; class ThumbView; + FRIEND_TEST_ALL_PREFIXES(ToggleButtonTest, AccessibilityTest); // Updates position of the thumb. void UpdateThumb();
diff --git a/ui/views/controls/button/toggle_button_unittest.cc b/ui/views/controls/button/toggle_button_unittest.cc index 59357c8..74d34aa 100644 --- a/ui/views/controls/button/toggle_button_unittest.cc +++ b/ui/views/controls/button/toggle_button_unittest.cc
@@ -152,4 +152,28 @@ EXPECT_FALSE(button()->GetIsOn()); } +TEST_F(ToggleButtonTest, AccessibilityTest) { + button()->SetAccessibleName(u"Name"); + ui::AXNodeData data; + button()->GetAccessibleNodeData(&data); + + EXPECT_EQ(data.GetString16Attribute(ax::mojom::StringAttribute::kName), + u"Name"); + EXPECT_EQ(button()->GetAccessibleName(), u"Name"); + EXPECT_EQ(data.role, ax::mojom::Role::kSwitch); + EXPECT_EQ(button()->GetAccessibleRole(), ax::mojom::Role::kSwitch); + EXPECT_EQ(data.GetCheckedState(), ax::mojom::CheckedState::kFalse); + + data = ui::AXNodeData(); + button()->SetIsOn(true); + button()->GetAccessibleNodeData(&data); + EXPECT_EQ(data.GetCheckedState(), ax::mojom::CheckedState::kTrue); + + data = ui::AXNodeData(); + button()->SetAccessibleRole(ax::mojom::Role::kCheckBox); + button()->GetAccessibleNodeData(&data); + EXPECT_EQ(data.role, ax::mojom::Role::kCheckBox); + EXPECT_EQ(button()->GetAccessibleRole(), ax::mojom::Role::kCheckBox); +} + } // namespace views
diff --git a/ui/views/examples/button_example.cc b/ui/views/examples/button_example.cc index 06158bd1..c1676b6 100644 --- a/ui/views/examples/button_example.cc +++ b/ui/views/examples/button_example.cc
@@ -250,6 +250,11 @@ base::Unretained(this)), u"Fab Prototype")); + view->AddChildView(ImageButton::CreateIconButton( + base::BindRepeating(&ButtonExample::ImageButtonPressed, + base::Unretained(this)), + views::kLaunchIcon, u"Icon button")); + image_button_->SetImage(ImageButton::STATE_NORMAL, rb.GetImageNamed(IDR_CLOSE).ToImageSkia()); image_button_->SetImage(ImageButton::STATE_HOVERED,
diff --git a/ui/views/layout/layout_provider.cc b/ui/views/layout/layout_provider.cc index 8437744..d6885dc 100644 --- a/ui/views/layout/layout_provider.cc +++ b/ui/views/layout/layout_provider.cc
@@ -69,6 +69,8 @@ return gfx::Insets(4); case InsetsMetric::INSETS_LABEL_BUTTON: return gfx::Insets::VH(5, 6); + case InsetsMetric::INSETS_ICON_BUTTON: + return gfx::Insets(2); } NOTREACHED_NORETURN(); } @@ -121,6 +123,8 @@ return features::IsChromeRefresh2023() ? 10 : 8; case DISTANCE_UNRELATED_CONTROL_VERTICAL: return 16; + case DISTANCE_VECTOR_ICON_PADDING: + return 4; case VIEWS_DISTANCE_END: case VIEWS_DISTANCE_MAX: NOTREACHED_NORETURN();
diff --git a/ui/views/layout/layout_provider.h b/ui/views/layout/layout_provider.h index b432a88..a15febe2 100644 --- a/ui/views/layout/layout_provider.h +++ b/ui/views/layout/layout_provider.h
@@ -40,6 +40,8 @@ INSETS_VECTOR_IMAGE_BUTTON, // Padding used in a label button. INSETS_LABEL_BUTTON, + // Padding used in icon buttons. + INSETS_ICON_BUTTON, // Embedders must start Insets enum values from this value. VIEWS_INSETS_END, @@ -105,6 +107,8 @@ DISTANCE_TEXTFIELD_HORIZONTAL_TEXT_PADDING, // Vertical spacing between controls that are logically unrelated. DISTANCE_UNRELATED_CONTROL_VERTICAL, + // Padding in vector icons. This is a general number for more vector icons. + DISTANCE_VECTOR_ICON_PADDING, // Embedders must start DistanceMetric enum values from here. VIEWS_DISTANCE_END,
diff --git a/ui/webui/resources/cr_elements/chromeos/cros_color_overrides.css b/ui/webui/resources/cr_elements/chromeos/cros_color_overrides.css index 6fa6ef5..07bf428 100644 --- a/ui/webui/resources/cr_elements/chromeos/cros_color_overrides.css +++ b/ui/webui/resources/cr_elements/chromeos/cros_color_overrides.css
@@ -167,7 +167,7 @@ /* Dialog */ :host-context(body.jelly-enabled) cr-dialog::part(dialog) { - --cr-dialog-background-color: var(--cros-sys-app_base_elevated); + --cr-dialog-background-color: var(--cros-sys-base_elevated); background-image: none; /* TODO(b/266837484) Replace with cros.sys.app-elevation3 when available */ @@ -185,7 +185,7 @@ :host-context(body.jelly-enabled) cr-input, :host-context(body.jelly-enabled) cr-search-field::part(searchInput), :host-context(body.jelly-enabled) cr-textarea { - --cr-input-background-color: var(--cros-sys-input_field_dark); + --cr-input-background-color: var(--cros-sys-input_field_on_base); --cr-input-error-color: var(--cros-sys-error); --cr-input-focus-color: var(--cros-sys-primary); --cr-input-placeholder-color: var(--cros-sys-secondary); @@ -193,9 +193,9 @@ /* md-select */ :host-context(body.jelly-enabled) .md-select { - --md-select-bg-color: var(--cros-sys-input_field_dark); + --md-select-bg-color: var(--cros-sys-input_field_on_base); --md-select-focus-shadow-color: var(--cros-sys-primary); - --md-select-option-bg-color: var(--cros-sys-app_base_elevated); + --md-select-option-bg-color: var(--cros-sys-base_elevated); --md-select-text-color: var(--cros-sys-on_surface); }
diff --git a/ui/webui/resources/css/text_defaults_md.css b/ui/webui/resources/css/text_defaults_md.css index 5bb3cfe0..13015eb 100644 --- a/ui/webui/resources/css/text_defaults_md.css +++ b/ui/webui/resources/css/text_defaults_md.css
@@ -20,10 +20,10 @@ </if> body { - font-family: Roboto, $i18nRaw{fontfamily}; + font-family: $i18nRaw{fontfamilyMd}; font-size: 81.25%; } button { - font-family: Roboto, $i18nRaw{fontfamily}; + font-family: $i18nRaw{fontfamilyMd}; }
diff --git a/weblayer/browser/android/javatests/BUILD.gn b/weblayer/browser/android/javatests/BUILD.gn index 6246a7f5..33d0088e 100644 --- a/weblayer/browser/android/javatests/BUILD.gn +++ b/weblayer/browser/android/javatests/BUILD.gn
@@ -32,14 +32,14 @@ "//net/android:net_java_test_support", "//third_party/android_deps:com_google_guava_listenablefuture_java", "//third_party/android_deps:guava_android_java", - "//third_party/android_support_test_runner:rules_java", - "//third_party/android_support_test_runner:runner_java", "//third_party/androidx:androidx_activity_activity_java", "//third_party/androidx:androidx_annotation_annotation_java", "//third_party/androidx:androidx_appcompat_appcompat_java", "//third_party/androidx:androidx_core_core_java", "//third_party/androidx:androidx_fragment_fragment_java", "//third_party/androidx:androidx_test_core_java", + "//third_party/androidx:androidx_test_monitor_java", + "//third_party/androidx:androidx_test_rules_java", "//third_party/androidx:androidx_test_runner_java", "//third_party/blink/public/common:common_java", "//third_party/hamcrest:hamcrest_core_java", @@ -64,10 +64,11 @@ "//content/public/test/android:content_java_test_support", "//net/android:net_java_test_support", "//third_party/android_deps:com_google_guava_listenablefuture_java", - "//third_party/android_support_test_runner:rules_java", - "//third_party/android_support_test_runner:runner_java", "//third_party/androidx:androidx_annotation_annotation_java", "//third_party/androidx:androidx_fragment_fragment_java", + "//third_party/androidx:androidx_test_monitor_java", + "//third_party/androidx:androidx_test_rules_java", + "//third_party/androidx:androidx_test_runner_java", "//third_party/junit:junit", "//ui/android:ui_java_test_support", "//weblayer/public/java:webengine_java",
diff --git a/weblayer/browser/android/javatests/src/org/chromium/webengine/test/FullscreenCallbackTest.java b/weblayer/browser/android/javatests/src/org/chromium/webengine/test/FullscreenCallbackTest.java index 13f0f16f..9405254 100644 --- a/weblayer/browser/android/javatests/src/org/chromium/webengine/test/FullscreenCallbackTest.java +++ b/weblayer/browser/android/javatests/src/org/chromium/webengine/test/FullscreenCallbackTest.java
@@ -6,8 +6,7 @@ import static org.chromium.content_public.browser.test.util.TestThreadUtils.runOnUiThreadBlocking; -import android.support.test.InstrumentationRegistry; - +import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import org.junit.After;
diff --git a/weblayer/browser/android/javatests/src/org/chromium/webengine/test/InstrumentationActivityTestRule.java b/weblayer/browser/android/javatests/src/org/chromium/webengine/test/InstrumentationActivityTestRule.java index c9973b3..fe2ccd7 100644 --- a/weblayer/browser/android/javatests/src/org/chromium/webengine/test/InstrumentationActivityTestRule.java +++ b/weblayer/browser/android/javatests/src/org/chromium/webengine/test/InstrumentationActivityTestRule.java
@@ -9,10 +9,10 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; -import android.support.test.InstrumentationRegistry; import android.view.View; import androidx.annotation.Nullable; +import androidx.test.InstrumentationRegistry; import org.junit.Assert;
diff --git a/weblayer/browser/android/javatests/src/org/chromium/webengine/test/WebEngineActivityTestRule.java b/weblayer/browser/android/javatests/src/org/chromium/webengine/test/WebEngineActivityTestRule.java index 264af1b..c98dcb6 100644 --- a/weblayer/browser/android/javatests/src/org/chromium/webengine/test/WebEngineActivityTestRule.java +++ b/weblayer/browser/android/javatests/src/org/chromium/webengine/test/WebEngineActivityTestRule.java
@@ -6,9 +6,10 @@ import android.app.Activity; import android.content.Context; -import android.support.test.InstrumentationRegistry; import android.text.TextUtils; +import androidx.test.InstrumentationRegistry; + import org.junit.runner.Description; import org.junit.runners.model.Statement;
diff --git a/weblayer/public/javatests/BUILD.gn b/weblayer/public/javatests/BUILD.gn index c1b1bde..184207d1 100644 --- a/weblayer/public/javatests/BUILD.gn +++ b/weblayer/public/javatests/BUILD.gn
@@ -13,7 +13,7 @@ ] deps = [ "//base:base_java_test_support", - "//third_party/android_support_test_runner:runner_java", + "//third_party/androidx:androidx_test_monitor_java", "//third_party/androidx:androidx_test_runner_java", "//third_party/junit:junit", "//weblayer/public/java",
diff --git a/weblayer/public/javatests/org/chromium/weblayer/WebViewCompatibilityHelperTest.java b/weblayer/public/javatests/org/chromium/weblayer/WebViewCompatibilityHelperTest.java index 963fde3..f7186ec09 100644 --- a/weblayer/public/javatests/org/chromium/weblayer/WebViewCompatibilityHelperTest.java +++ b/weblayer/public/javatests/org/chromium/weblayer/WebViewCompatibilityHelperTest.java
@@ -5,8 +5,8 @@ package org.chromium.weblayer; import android.content.Context; -import android.support.test.InstrumentationRegistry; +import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import org.junit.Assert;