Revert "Reland "[Default Nav Transition] Remove top controls in NTP screenshot""
This reverts commit26c52b7ed0
. Reason for revert: Test added is consistently failing. See details in https://b.corp.google.com/issues/331814846#comment18. Original change's description: > Reland "[Default Nav Transition] Remove top controls in NTP screenshot" > > This reverts commitc2fd4d5853
. > > The tests are using NavigationHandler which only > works in 3-button nav mode and the test failed on OS > which defaults to gesture nav mode. > > Original change's description: > > Revert "[Default Nav Transition] Remove top controls in NTP screenshot" > > > > This reverts commit7938c61cf2
. > > > > Reason for revert: Causing bots to fail on CI: https://crbug.com/351979006 > > > > Original change's description: > > > [Default Nav Transition] Remove top controls in NTP screenshot > > > > > > In order to match the viewport of the webpage screenshot, this CL > > > excludes the area of top controls of NTP and maintain the > > > size of the screenshot. As a result, the bottom part of the screenshot > > > will be filled with the default background color of the NTP because > > > it is impossible to draw off-screen views in Android. > > > > > > Screenshot (with top controls): https://screenshot.googleplex.com/AJFcDYvXCbnvXgj.png > > > Screen record (w/o top controls): http://screencast/cast/NDY4NTA0OTc2ODE4MTc2MHxjODkyNWE0Zi1jOQ > > > > > > Bug: 331814846 > > > Change-Id: Ieb8959ad2f202b15e055346f889a73355cdc4e06 > > > Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5560151 > > > Reviewed-by: Rakina Zata Amni <rakina@chromium.org> > > > Code-Coverage: findit-for-me@appspot.gserviceaccount.com <findit-for-me@appspot.gserviceaccount.com> > > > Reviewed-by: Khushal Sagar <khushalsagar@chromium.org> > > > Commit-Queue: Lijin Shen <lazzzis@google.com> > > > Reviewed-by: Calder Kitagawa <ckitagawa@chromium.org> > > > Cr-Commit-Position: refs/heads/main@{#1324606} > > > > Bug: 331814846 > > Change-Id: Idee7cef1905d173ae74233fb5aa566e58f070691 > > No-Presubmit: true > > No-Tree-Checks: true > > No-Try: true > > Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5687334 > > Owners-Override: Rupert Wiser <bewise@chromium.org> > > Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com> > > Commit-Queue: Rupert Wiser <bewise@chromium.org> > > Cr-Commit-Position: refs/heads/main@{#1324742} > > Bug: 331814846, 351979006 > Change-Id: Iac27e3d8cba61a2312d457bedcb61d16048ae5f4 > Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5689652 > Reviewed-by: Rakina Zata Amni <rakina@chromium.org> > Reviewed-by: Rupert Wiser <bewise@chromium.org> > Commit-Queue: Lijin Shen <lazzzis@google.com> > Reviewed-by: Calder Kitagawa <ckitagawa@chromium.org> > Reviewed-by: Khushal Sagar <khushalsagar@chromium.org> > Cr-Commit-Position: refs/heads/main@{#1326304} Bug: 331814846, 351979006 Change-Id: I16afc5760780350eb4d22635cc1814807d5bee2d No-Presubmit: true No-Tree-Checks: true No-Try: true Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5697875 Reviewed-by: Yue Zhang <yuezhanggg@chromium.org> Commit-Queue: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com> Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com> Auto-Submit: Yue Zhang <yuezhanggg@chromium.org> Owners-Override: Yue Zhang <yuezhanggg@chromium.org> Cr-Commit-Position: refs/heads/main@{#1326454}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
ef915b42e4
commit
918efe49d3
chrome
android
java
src
org
chromium
chrome
browser
javatests
src
org
chromium
chrome
browser
browser
gesturenav
android
content
browser
web_contents
public
android
java
src
org
chromium
test
android
javatests
src
org
chromium
content_public
browser
test
@ -429,8 +429,7 @@ final class TabWebContentsDelegateAndroidImpl extends TabWebContentsDelegateAndr
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean maybeCopyContentAreaAsBitmap(Callback<Bitmap> callback) {
|
public boolean maybeCopyContentAreaAsBitmap(Callback<Bitmap> callback) {
|
||||||
return NativePageBitmapCapturer.maybeCaptureNativeView(
|
return NativePageBitmapCapturer.maybeCaptureNativeView(mTab, callback);
|
||||||
mTab, callback, getTopControlsHeight());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -47,7 +47,6 @@ import org.chromium.chrome.browser.ui.native_page.BasicSmoothTransitionDelegate;
|
|||||||
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
|
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
|
||||||
import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
|
import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
|
||||||
import org.chromium.chrome.test.util.ChromeTabUtils;
|
import org.chromium.chrome.test.util.ChromeTabUtils;
|
||||||
import org.chromium.chrome.test.util.NewTabPageTestUtils;
|
|
||||||
import org.chromium.components.embedder_support.util.UrlConstants;
|
import org.chromium.components.embedder_support.util.UrlConstants;
|
||||||
import org.chromium.content_public.browser.LoadUrlParams;
|
import org.chromium.content_public.browser.LoadUrlParams;
|
||||||
import org.chromium.content_public.browser.back_forward_transition.AnimationStage;
|
import org.chromium.content_public.browser.back_forward_transition.AnimationStage;
|
||||||
@ -71,7 +70,6 @@ import java.util.concurrent.TimeoutException;
|
|||||||
@Batch(Batch.PER_CLASS)
|
@Batch(Batch.PER_CLASS)
|
||||||
public class NavigationHandlerTest {
|
public class NavigationHandlerTest {
|
||||||
private static final String RENDERED_PAGE = "/chrome/test/data/android/navigate/simple.html";
|
private static final String RENDERED_PAGE = "/chrome/test/data/android/navigate/simple.html";
|
||||||
private static final String TEST_PAGE = "/chrome/test/data/android/test.html";
|
|
||||||
private static final boolean LEFT_EDGE = true;
|
private static final boolean LEFT_EDGE = true;
|
||||||
private static final boolean RIGHT_EDGE = false;
|
private static final boolean RIGHT_EDGE = false;
|
||||||
|
|
||||||
@ -86,10 +84,7 @@ public class NavigationHandlerTest {
|
|||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() throws InterruptedException {
|
public void setUp() throws InterruptedException {
|
||||||
mTestServer =
|
mActivityTestRule.startMainActivityOnBlankPage();
|
||||||
EmbeddedTestServer.createAndStartServer(
|
|
||||||
InstrumentationRegistry.getInstrumentation().getContext());
|
|
||||||
mActivityTestRule.startMainActivityWithURL(mTestServer.getURL(TEST_PAGE));
|
|
||||||
CompositorAnimationHandler.setTestingMode(true);
|
CompositorAnimationHandler.setTestingMode(true);
|
||||||
mNavUtils = new GestureNavigationUtils(mActivityTestRule);
|
mNavUtils = new GestureNavigationUtils(mActivityTestRule);
|
||||||
mNavigationHandler = mNavUtils.getNavigationHandler();
|
mNavigationHandler = mNavUtils.getNavigationHandler();
|
||||||
@ -130,12 +125,12 @@ public class NavigationHandlerTest {
|
|||||||
@Test
|
@Test
|
||||||
@SmallTest
|
@SmallTest
|
||||||
@EnableFeatures({ChromeFeatureList.BACK_FORWARD_TRANSITIONS})
|
@EnableFeatures({ChromeFeatureList.BACK_FORWARD_TRANSITIONS})
|
||||||
public void testSwipeBackToNTPWithTransition() throws InterruptedException {
|
public void testSwipeBackToNTPWithTransition() {
|
||||||
UiUtils.settleDownUI(InstrumentationRegistry.getInstrumentation());
|
|
||||||
final Tab tab = mActivityTestRule.getActivity().getActivityTab();
|
final Tab tab = mActivityTestRule.getActivity().getActivityTab();
|
||||||
mActivityTestRule.loadUrl(UrlConstants.NTP_URL);
|
mActivityTestRule.loadUrl(UrlConstants.NTP_URL);
|
||||||
UiUtils.settleDownUI(InstrumentationRegistry.getInstrumentation());
|
mTestServer =
|
||||||
NewTabPageTestUtils.waitForNtpLoaded(mActivityTestRule.getActivity().getActivityTab());
|
EmbeddedTestServer.createAndStartServer(
|
||||||
|
InstrumentationRegistry.getInstrumentation().getContext());
|
||||||
mActivityTestRule.loadUrl(mTestServer.getURL(RENDERED_PAGE));
|
mActivityTestRule.loadUrl(mTestServer.getURL(RENDERED_PAGE));
|
||||||
|
|
||||||
mNavUtils.swipeFromEdgeAndHold(true);
|
mNavUtils.swipeFromEdgeAndHold(true);
|
||||||
@ -177,6 +172,9 @@ public class NavigationHandlerTest {
|
|||||||
final Tab tab = mActivityTestRule.getActivity().getActivityTab();
|
final Tab tab = mActivityTestRule.getActivity().getActivityTab();
|
||||||
mActivityTestRule.loadUrl("chrome-native://bookmarks/folder/0");
|
mActivityTestRule.loadUrl("chrome-native://bookmarks/folder/0");
|
||||||
UiUtils.settleDownUI(InstrumentationRegistry.getInstrumentation());
|
UiUtils.settleDownUI(InstrumentationRegistry.getInstrumentation());
|
||||||
|
mTestServer =
|
||||||
|
EmbeddedTestServer.createAndStartServer(
|
||||||
|
InstrumentationRegistry.getInstrumentation().getContext());
|
||||||
mActivityTestRule.loadUrl(mTestServer.getURL(RENDERED_PAGE));
|
mActivityTestRule.loadUrl(mTestServer.getURL(RENDERED_PAGE));
|
||||||
|
|
||||||
mNavUtils.swipeFromEdgeAndHold(true);
|
mNavUtils.swipeFromEdgeAndHold(true);
|
||||||
@ -253,6 +251,9 @@ public class NavigationHandlerTest {
|
|||||||
@Test
|
@Test
|
||||||
@SmallTest
|
@SmallTest
|
||||||
public void testReleaseGlowWithoutPrecedingPullIgnored() {
|
public void testReleaseGlowWithoutPrecedingPullIgnored() {
|
||||||
|
mTestServer =
|
||||||
|
EmbeddedTestServer.createAndStartServer(
|
||||||
|
InstrumentationRegistry.getInstrumentation().getContext());
|
||||||
mActivityTestRule.loadUrl(mTestServer.getURL(RENDERED_PAGE));
|
mActivityTestRule.loadUrl(mTestServer.getURL(RENDERED_PAGE));
|
||||||
TestThreadUtils.runOnUiThreadBlocking(
|
TestThreadUtils.runOnUiThreadBlocking(
|
||||||
() -> {
|
() -> {
|
||||||
@ -315,6 +316,9 @@ public class NavigationHandlerTest {
|
|||||||
public void testSwipeNavigateOnRenderedPage() {
|
public void testSwipeNavigateOnRenderedPage() {
|
||||||
// TODO(crbug.com/40899221): Write a test variation running with
|
// TODO(crbug.com/40899221): Write a test variation running with
|
||||||
// ChromeFeatureList.BACK_FORWARD_TRANSITIONS enabled when the feature is completed.
|
// ChromeFeatureList.BACK_FORWARD_TRANSITIONS enabled when the feature is completed.
|
||||||
|
mTestServer =
|
||||||
|
EmbeddedTestServer.createAndStartServer(
|
||||||
|
InstrumentationRegistry.getInstrumentation().getContext());
|
||||||
mActivityTestRule.loadUrl(mTestServer.getURL(RENDERED_PAGE));
|
mActivityTestRule.loadUrl(mTestServer.getURL(RENDERED_PAGE));
|
||||||
mActivityTestRule.loadUrl(ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL);
|
mActivityTestRule.loadUrl(ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL);
|
||||||
|
|
||||||
@ -369,6 +373,9 @@ public class NavigationHandlerTest {
|
|||||||
@Test
|
@Test
|
||||||
@SmallTest
|
@SmallTest
|
||||||
public void testSwipeAfterDestroy() {
|
public void testSwipeAfterDestroy() {
|
||||||
|
mTestServer =
|
||||||
|
EmbeddedTestServer.createAndStartServer(
|
||||||
|
InstrumentationRegistry.getInstrumentation().getContext());
|
||||||
mActivityTestRule.loadUrl(mTestServer.getURL(RENDERED_PAGE));
|
mActivityTestRule.loadUrl(mTestServer.getURL(RENDERED_PAGE));
|
||||||
TestThreadUtils.runOnUiThreadBlocking(mNavigationHandler::destroy);
|
TestThreadUtils.runOnUiThreadBlocking(mNavigationHandler::destroy);
|
||||||
|
|
||||||
@ -388,6 +395,9 @@ public class NavigationHandlerTest {
|
|||||||
@Test
|
@Test
|
||||||
@SmallTest
|
@SmallTest
|
||||||
public void testSwipeAfterTabDestroy() {
|
public void testSwipeAfterTabDestroy() {
|
||||||
|
mTestServer =
|
||||||
|
EmbeddedTestServer.createAndStartServer(
|
||||||
|
InstrumentationRegistry.getInstrumentation().getContext());
|
||||||
mActivityTestRule.loadUrl(mTestServer.getURL(RENDERED_PAGE));
|
mActivityTestRule.loadUrl(mTestServer.getURL(RENDERED_PAGE));
|
||||||
TestThreadUtils.runOnUiThreadBlocking(currentTab()::destroy);
|
TestThreadUtils.runOnUiThreadBlocking(currentTab()::destroy);
|
||||||
|
|
||||||
@ -401,6 +411,9 @@ public class NavigationHandlerTest {
|
|||||||
@Test
|
@Test
|
||||||
@SmallTest
|
@SmallTest
|
||||||
public void testSwipeAfterDestroyActivity_NativePage() {
|
public void testSwipeAfterDestroyActivity_NativePage() {
|
||||||
|
mTestServer =
|
||||||
|
EmbeddedTestServer.createAndStartServer(
|
||||||
|
InstrumentationRegistry.getInstrumentation().getContext());
|
||||||
mActivityTestRule.loadUrl(UrlConstants.NTP_URL);
|
mActivityTestRule.loadUrl(UrlConstants.NTP_URL);
|
||||||
TestThreadUtils.runOnUiThreadBlocking(mActivityTestRule.getActivity()::finish);
|
TestThreadUtils.runOnUiThreadBlocking(mActivityTestRule.getActivity()::finish);
|
||||||
|
|
||||||
|
@ -5,7 +5,6 @@
|
|||||||
package org.chromium.chrome.browser.gesturenav;
|
package org.chromium.chrome.browser.gesturenav;
|
||||||
|
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.os.Build.VERSION_CODES;
|
|
||||||
|
|
||||||
import androidx.test.core.app.ApplicationProvider;
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
import androidx.test.filters.MediumTest;
|
import androidx.test.filters.MediumTest;
|
||||||
@ -24,25 +23,19 @@ import org.chromium.base.test.params.ParameterAnnotations;
|
|||||||
import org.chromium.base.test.params.ParameterizedRunner;
|
import org.chromium.base.test.params.ParameterizedRunner;
|
||||||
import org.chromium.base.test.util.CallbackHelper;
|
import org.chromium.base.test.util.CallbackHelper;
|
||||||
import org.chromium.base.test.util.CommandLineFlags;
|
import org.chromium.base.test.util.CommandLineFlags;
|
||||||
import org.chromium.base.test.util.DisableIf;
|
|
||||||
import org.chromium.base.test.util.DoNotBatch;
|
import org.chromium.base.test.util.DoNotBatch;
|
||||||
import org.chromium.base.test.util.Feature;
|
import org.chromium.base.test.util.Feature;
|
||||||
import org.chromium.base.test.util.Features.EnableFeatures;
|
import org.chromium.base.test.util.Features.EnableFeatures;
|
||||||
import org.chromium.base.test.util.UrlUtils;
|
|
||||||
import org.chromium.blink_public.common.BlinkFeatures;
|
import org.chromium.blink_public.common.BlinkFeatures;
|
||||||
import org.chromium.chrome.browser.flags.ChromeFeatureList;
|
import org.chromium.chrome.browser.flags.ChromeFeatureList;
|
||||||
import org.chromium.chrome.browser.flags.ChromeSwitches;
|
import org.chromium.chrome.browser.flags.ChromeSwitches;
|
||||||
import org.chromium.chrome.browser.fullscreen.BrowserControlsManager;
|
|
||||||
import org.chromium.chrome.browser.fullscreen.FullscreenManagerTestUtils;
|
|
||||||
import org.chromium.chrome.browser.night_mode.ChromeNightModeTestUtils;
|
import org.chromium.chrome.browser.night_mode.ChromeNightModeTestUtils;
|
||||||
import org.chromium.chrome.browser.tab.TabStateBrowserControlsVisibilityDelegate;
|
|
||||||
import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
|
import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
|
||||||
import org.chromium.chrome.test.util.ChromeRenderTestRule;
|
import org.chromium.chrome.test.util.ChromeRenderTestRule;
|
||||||
import org.chromium.chrome.test.util.NewTabPageTestUtils;
|
import org.chromium.chrome.test.util.NewTabPageTestUtils;
|
||||||
import org.chromium.chrome.test.util.browser.suggestions.SuggestionsDependenciesRule;
|
import org.chromium.chrome.test.util.browser.suggestions.SuggestionsDependenciesRule;
|
||||||
import org.chromium.chrome.test.util.browser.suggestions.mostvisited.FakeMostVisitedSites;
|
import org.chromium.chrome.test.util.browser.suggestions.mostvisited.FakeMostVisitedSites;
|
||||||
import org.chromium.components.embedder_support.util.UrlConstants;
|
import org.chromium.components.embedder_support.util.UrlConstants;
|
||||||
import org.chromium.content_public.browser.test.util.TestThreadUtils;
|
|
||||||
import org.chromium.content_public.browser.test.util.UiUtils;
|
import org.chromium.content_public.browser.test.util.UiUtils;
|
||||||
import org.chromium.content_public.browser.test.util.WebContentsUtils;
|
import org.chromium.content_public.browser.test.util.WebContentsUtils;
|
||||||
import org.chromium.net.test.EmbeddedTestServer;
|
import org.chromium.net.test.EmbeddedTestServer;
|
||||||
@ -77,15 +70,6 @@ public class ScreenshotCaptureTest {
|
|||||||
|
|
||||||
private static final String TEST_PAGE = "/chrome/test/data/android/simple.html";
|
private static final String TEST_PAGE = "/chrome/test/data/android/simple.html";
|
||||||
private static final String TEST_PAGE_2 = "/chrome/test/data/android/google.html";
|
private static final String TEST_PAGE_2 = "/chrome/test/data/android/google.html";
|
||||||
private static final String LONG_HTML_TEST_PAGE =
|
|
||||||
UrlUtils.encodeHtmlDataUri(
|
|
||||||
"<html>"
|
|
||||||
+ "<head>"
|
|
||||||
+ " <meta name=\"viewport\" content=\"width=device-width\">"
|
|
||||||
+ "</head>"
|
|
||||||
+ "<body style='height:100000px;'>"
|
|
||||||
+ "</body>"
|
|
||||||
+ "</html>");
|
|
||||||
|
|
||||||
private EmbeddedTestServer mTestServer;
|
private EmbeddedTestServer mTestServer;
|
||||||
private Bitmap mCapturedBitmap;
|
private Bitmap mCapturedBitmap;
|
||||||
@ -109,10 +93,9 @@ public class ScreenshotCaptureTest {
|
|||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
// Fix the port cause the screenshot includes the url bar
|
|
||||||
mTestServer =
|
mTestServer =
|
||||||
EmbeddedTestServer.createAndStartServerWithPort(
|
EmbeddedTestServer.createAndStartServer(
|
||||||
ApplicationProvider.getApplicationContext(), 46985);
|
ApplicationProvider.getApplicationContext());
|
||||||
|
|
||||||
var mSiteSuggestions = NewTabPageTestUtils.createFakeSiteSuggestions(mTestServer);
|
var mSiteSuggestions = NewTabPageTestUtils.createFakeSiteSuggestions(mTestServer);
|
||||||
var mMostVisitedSites = new FakeMostVisitedSites();
|
var mMostVisitedSites = new FakeMostVisitedSites();
|
||||||
@ -206,91 +189,6 @@ public class ScreenshotCaptureTest {
|
|||||||
mRenderTestRule.compareForResult(mCapturedBitmap, "navigate_away_from_ntp_to_webui_page");
|
mRenderTestRule.compareForResult(mCapturedBitmap, "navigate_away_from_ntp_to_webui_page");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
@MediumTest
|
|
||||||
@Feature({"RenderTest"})
|
|
||||||
// The test is based on 3-button mode. The newer version defaults to gesture mode.
|
|
||||||
@DisableIf.Build(sdk_is_greater_than = VERSION_CODES.R)
|
|
||||||
@ParameterAnnotations.UseMethodParameter(NightModeTestUtils.NightModeParams.class)
|
|
||||||
public void testNavigatingBackToNtpFromNormalPage(boolean nightModeEnabled)
|
|
||||||
throws IOException, TimeoutException, InterruptedException {
|
|
||||||
mActivityTestRule.startMainActivityWithURL(UrlConstants.NTP_URL);
|
|
||||||
UiUtils.settleDownUI(InstrumentationRegistry.getInstrumentation());
|
|
||||||
NewTabPageTestUtils.waitForNtpLoaded(mActivityTestRule.getActivity().getActivityTab());
|
|
||||||
|
|
||||||
mActivityTestRule.loadUrl(mTestServer.getURL(TEST_PAGE));
|
|
||||||
|
|
||||||
GestureNavigationUtils mNavUtils = new GestureNavigationUtils(mActivityTestRule);
|
|
||||||
mNavUtils.swipeFromEdgeAndHold(/* leftEdge= */ true);
|
|
||||||
|
|
||||||
CallbackHelper callbackHelper = new CallbackHelper();
|
|
||||||
mActivityTestRule
|
|
||||||
.getWebContents()
|
|
||||||
.captureContentAsBitmapForTesting(
|
|
||||||
bitmap -> {
|
|
||||||
try {
|
|
||||||
mRenderTestRule.compareForResult(
|
|
||||||
bitmap, "navigate_back_to_ntp_from_normal_page");
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
callbackHelper.notifyCalled();
|
|
||||||
});
|
|
||||||
callbackHelper.waitForOnly();
|
|
||||||
TestThreadUtils.runOnUiThreadBlocking(() -> mNavUtils.getNavigationHandler().release(true));
|
|
||||||
// Wait animation to be finished. Reduce flakiness caused by being destroyed during a
|
|
||||||
// running animation.
|
|
||||||
UiUtils.settleDownUI(InstrumentationRegistry.getInstrumentation());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@MediumTest
|
|
||||||
@Feature({"RenderTest"})
|
|
||||||
// The test is based on 3-button mode. The newer version defaults to gesture mode.
|
|
||||||
@DisableIf.Build(sdk_is_greater_than = VERSION_CODES.R)
|
|
||||||
@ParameterAnnotations.UseMethodParameter(NightModeTestUtils.NightModeParams.class)
|
|
||||||
public void testNavigatingBackToNtpFromNormalPageWithoutTopControls(boolean nightModeEnabled)
|
|
||||||
throws Throwable {
|
|
||||||
TestThreadUtils.runOnUiThreadBlocking(
|
|
||||||
TabStateBrowserControlsVisibilityDelegate::disablePageLoadDelayForTests);
|
|
||||||
mActivityTestRule.startMainActivityWithURL(UrlConstants.NTP_URL);
|
|
||||||
UiUtils.settleDownUI(InstrumentationRegistry.getInstrumentation());
|
|
||||||
NewTabPageTestUtils.waitForNtpLoaded(mActivityTestRule.getActivity().getActivityTab());
|
|
||||||
FullscreenManagerTestUtils.disableBrowserOverrides();
|
|
||||||
mActivityTestRule.loadUrl(LONG_HTML_TEST_PAGE);
|
|
||||||
BrowserControlsManager browserControlManager =
|
|
||||||
mActivityTestRule.getActivity().getBrowserControlsManager();
|
|
||||||
int browserControlsHeight = browserControlManager.getTopControlsHeight();
|
|
||||||
FullscreenManagerTestUtils.waitForBrowserControlsToBeMoveable(
|
|
||||||
mActivityTestRule, mActivityTestRule.getActivity().getActivityTab());
|
|
||||||
FullscreenManagerTestUtils.scrollBrowserControls(mActivityTestRule, false);
|
|
||||||
|
|
||||||
FullscreenManagerTestUtils.waitForBrowserControlsPosition(
|
|
||||||
mActivityTestRule, -browserControlsHeight);
|
|
||||||
GestureNavigationUtils mNavUtils = new GestureNavigationUtils(mActivityTestRule);
|
|
||||||
mNavUtils.swipeFromEdgeAndHold(/* leftEdge= */ true);
|
|
||||||
|
|
||||||
CallbackHelper callbackHelper = new CallbackHelper();
|
|
||||||
mActivityTestRule
|
|
||||||
.getWebContents()
|
|
||||||
.captureContentAsBitmapForTesting(
|
|
||||||
bitmap -> {
|
|
||||||
try {
|
|
||||||
mRenderTestRule.compareForResult(
|
|
||||||
bitmap,
|
|
||||||
"navigate_back_to_ntp_from_normal_page_without_top_controls");
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
callbackHelper.notifyCalled();
|
|
||||||
});
|
|
||||||
callbackHelper.waitForOnly();
|
|
||||||
TestThreadUtils.runOnUiThreadBlocking(() -> mNavUtils.getNavigationHandler().release(true));
|
|
||||||
// Wait animation to be finished. Reduce flakiness caused by being destroyed during a
|
|
||||||
// running animation.
|
|
||||||
UiUtils.settleDownUI(InstrumentationRegistry.getInstrumentation());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@MediumTest
|
@MediumTest
|
||||||
public void testNotCaptureSadTab() throws TimeoutException, InterruptedException {
|
public void testNotCaptureSadTab() throws TimeoutException, InterruptedException {
|
||||||
|
@ -15,7 +15,6 @@ android_library("java") {
|
|||||||
"//base:base_java",
|
"//base:base_java",
|
||||||
"//chrome/browser/flags:java",
|
"//chrome/browser/flags:java",
|
||||||
"//chrome/browser/tab:java",
|
"//chrome/browser/tab:java",
|
||||||
"//chrome/browser/ui/android/native_page:java",
|
|
||||||
"//third_party/androidx:androidx_annotation_annotation_java",
|
"//third_party/androidx:androidx_annotation_annotation_java",
|
||||||
"//third_party/jni_zero:jni_zero_java",
|
"//third_party/jni_zero:jni_zero_java",
|
||||||
"//ui/android:ui_no_recycler_view_java",
|
"//ui/android:ui_no_recycler_view_java",
|
||||||
|
@ -5,6 +5,7 @@ package org.chromium.chrome.browser.gesturenav;
|
|||||||
|
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Rect;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
@ -17,7 +18,7 @@ import org.chromium.base.task.PostTask;
|
|||||||
import org.chromium.base.task.TaskTraits;
|
import org.chromium.base.task.TaskTraits;
|
||||||
import org.chromium.chrome.browser.flags.ChromeFeatureList;
|
import org.chromium.chrome.browser.flags.ChromeFeatureList;
|
||||||
import org.chromium.chrome.browser.tab.Tab;
|
import org.chromium.chrome.browser.tab.Tab;
|
||||||
import org.chromium.ui.resources.dynamics.CaptureUtils;
|
import org.chromium.ui.resources.dynamics.CaptureObserver;
|
||||||
import org.chromium.ui.resources.dynamics.SoftwareDraw;
|
import org.chromium.ui.resources.dynamics.SoftwareDraw;
|
||||||
|
|
||||||
/** Capture native page as a bitmap. */
|
/** Capture native page as a bitmap. */
|
||||||
@ -36,11 +37,10 @@ public class NativePageBitmapCapturer implements UnownedUserData {
|
|||||||
* @param tab The target tab to be captured.
|
* @param tab The target tab to be captured.
|
||||||
* @param callback Executed with a non-null bitmap if the tab is presenting a native page. Empty
|
* @param callback Executed with a non-null bitmap if the tab is presenting a native page. Empty
|
||||||
* bitmap if capturing fails, such as out of memory error.
|
* bitmap if capturing fails, such as out of memory error.
|
||||||
* @param topControlsHeight Height of the top controls.
|
|
||||||
* @return True if the capture is successfully triggered; otherwise false.
|
* @return True if the capture is successfully triggered; otherwise false.
|
||||||
*/
|
*/
|
||||||
public static boolean maybeCaptureNativeView(
|
public static boolean maybeCaptureNativeView(
|
||||||
@NonNull Tab tab, @NonNull Callback<Bitmap> callback, int topControlsHeight) {
|
@NonNull Tab tab, @NonNull Callback<Bitmap> callback) {
|
||||||
if (!tab.isNativePage()) {
|
if (!tab.isNativePage()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -53,19 +53,25 @@ public class NativePageBitmapCapturer implements UnownedUserData {
|
|||||||
final var capturer = CAPTURER_KEY.retrieveDataFromHost(host);
|
final var capturer = CAPTURER_KEY.retrieveDataFromHost(host);
|
||||||
|
|
||||||
View view = tab.getView();
|
View view = tab.getView();
|
||||||
|
Rect viewBound = new Rect(0, 0, view.getWidth(), view.getHeight());
|
||||||
Bitmap bitmap = CaptureUtils.createBitmap(view.getWidth(), view.getHeight());
|
|
||||||
bitmap.eraseColor(tab.getNativePage().getBackgroundColor());
|
|
||||||
|
|
||||||
Canvas canvas = new Canvas(bitmap);
|
|
||||||
float scale = capturer.getScale();
|
|
||||||
|
|
||||||
// TODO(crbug.com/330230340): capture bitmap asynchronously.
|
// TODO(crbug.com/330230340): capture bitmap asynchronously.
|
||||||
// Translate to exclude the area of the top controls.
|
capturer.mSoftwareDraw.startBitmapCapture(
|
||||||
canvas.translate(0, -topControlsHeight);
|
view,
|
||||||
canvas.scale(scale, scale);
|
viewBound,
|
||||||
view.draw(canvas);
|
capturer.getScale(),
|
||||||
PostTask.postTask(TaskTraits.UI_USER_VISIBLE, () -> callback.onResult(bitmap));
|
new CaptureObserver() {
|
||||||
|
@Override
|
||||||
|
public void onCaptureStart(Canvas canvas, Rect dirtyRect) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCaptureEnd() {}
|
||||||
|
},
|
||||||
|
(bitmap) -> {
|
||||||
|
// The screenshot callback must be dispatched asynchronously. See
|
||||||
|
// WebContentsDelegateAndroid#maybeCopyContentAreaAsBitmap.
|
||||||
|
PostTask.postTask(TaskTraits.UI_USER_VISIBLE, () -> callback.onResult(bitmap));
|
||||||
|
});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,8 +19,6 @@ import org.chromium.base.test.util.CommandLineFlags;
|
|||||||
import org.chromium.base.test.util.Features;
|
import org.chromium.base.test.util.Features;
|
||||||
import org.chromium.chrome.browser.flags.ChromeFeatureList;
|
import org.chromium.chrome.browser.flags.ChromeFeatureList;
|
||||||
import org.chromium.chrome.browser.flags.ChromeSwitches;
|
import org.chromium.chrome.browser.flags.ChromeSwitches;
|
||||||
import org.chromium.chrome.browser.tab.TabTestUtils;
|
|
||||||
import org.chromium.chrome.browser.tab.TabWebContentsDelegateAndroid;
|
|
||||||
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
|
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
|
||||||
import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
|
import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
|
||||||
import org.chromium.components.embedder_support.util.UrlConstants;
|
import org.chromium.components.embedder_support.util.UrlConstants;
|
||||||
@ -41,12 +39,6 @@ public class NativePageBitmapCapturerTest {
|
|||||||
@SmallTest
|
@SmallTest
|
||||||
public void testWithNativePage() throws TimeoutException {
|
public void testWithNativePage() throws TimeoutException {
|
||||||
mTabbedActivityTestRule.startMainActivityWithURL(UrlConstants.NTP_URL);
|
mTabbedActivityTestRule.startMainActivityWithURL(UrlConstants.NTP_URL);
|
||||||
|
|
||||||
final TabWebContentsDelegateAndroid delegate =
|
|
||||||
TabTestUtils.getTabWebContentsDelegate(
|
|
||||||
mTabbedActivityTestRule.getActivity().getActivityTab());
|
|
||||||
final int topControlsHeight = delegate.getTopControlsHeight();
|
|
||||||
|
|
||||||
CallbackHelper callbackHelper = new CallbackHelper();
|
CallbackHelper callbackHelper = new CallbackHelper();
|
||||||
ThreadUtils.runOnUiThreadBlocking(
|
ThreadUtils.runOnUiThreadBlocking(
|
||||||
() -> {
|
() -> {
|
||||||
@ -55,8 +47,7 @@ public class NativePageBitmapCapturerTest {
|
|||||||
mTabbedActivityTestRule.getActivity().getActivityTab(),
|
mTabbedActivityTestRule.getActivity().getActivityTab(),
|
||||||
(bitmap) -> {
|
(bitmap) -> {
|
||||||
callbackHelper.notifyCalled();
|
callbackHelper.notifyCalled();
|
||||||
},
|
}));
|
||||||
topControlsHeight));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
callbackHelper.waitForOnly();
|
callbackHelper.waitForOnly();
|
||||||
@ -66,12 +57,6 @@ public class NativePageBitmapCapturerTest {
|
|||||||
@SmallTest
|
@SmallTest
|
||||||
public void testWithNonNativePage() {
|
public void testWithNonNativePage() {
|
||||||
mTabbedActivityTestRule.startMainActivityOnBlankPage();
|
mTabbedActivityTestRule.startMainActivityOnBlankPage();
|
||||||
|
|
||||||
final TabWebContentsDelegateAndroid delegate =
|
|
||||||
TabTestUtils.getTabWebContentsDelegate(
|
|
||||||
mTabbedActivityTestRule.getActivity().getActivityTab());
|
|
||||||
final int topControlsHeight = delegate.getTopControlsHeight();
|
|
||||||
|
|
||||||
CallbackHelper callbackHelper = new CallbackHelper();
|
CallbackHelper callbackHelper = new CallbackHelper();
|
||||||
ThreadUtils.runOnUiThreadBlocking(
|
ThreadUtils.runOnUiThreadBlocking(
|
||||||
() -> {
|
() -> {
|
||||||
@ -80,8 +65,7 @@ public class NativePageBitmapCapturerTest {
|
|||||||
mTabbedActivityTestRule.getActivity().getActivityTab(),
|
mTabbedActivityTestRule.getActivity().getActivityTab(),
|
||||||
(bitmap) -> {
|
(bitmap) -> {
|
||||||
callbackHelper.notifyCalled();
|
callbackHelper.notifyCalled();
|
||||||
},
|
}));
|
||||||
topControlsHeight));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Capture will be finished before the following task.
|
// Capture will be finished before the following task.
|
||||||
|
@ -15,7 +15,6 @@
|
|||||||
#include "base/android/jni_android.h"
|
#include "base/android/jni_android.h"
|
||||||
#include "base/android/jni_array.h"
|
#include "base/android/jni_array.h"
|
||||||
#include "base/android/jni_string.h"
|
#include "base/android/jni_string.h"
|
||||||
#include "base/check.h"
|
|
||||||
#include "base/functional/bind.h"
|
#include "base/functional/bind.h"
|
||||||
#include "base/functional/callback_helpers.h"
|
#include "base/functional/callback_helpers.h"
|
||||||
#include "base/json/json_writer.h"
|
#include "base/json/json_writer.h"
|
||||||
@ -49,7 +48,6 @@
|
|||||||
#include "ui/gfx/android/java_bitmap.h"
|
#include "ui/gfx/android/java_bitmap.h"
|
||||||
#include "ui/gfx/geometry/point.h"
|
#include "ui/gfx/geometry/point.h"
|
||||||
#include "ui/gfx/geometry/rect.h"
|
#include "ui/gfx/geometry/rect.h"
|
||||||
#include "ui/snapshot/snapshot.h"
|
|
||||||
#include "url/android/gurl_android.h"
|
#include "url/android/gurl_android.h"
|
||||||
#include "url/gurl.h"
|
#include "url/gurl.h"
|
||||||
|
|
||||||
@ -258,28 +256,6 @@ WebContentsAndroid::GetJavaObject() {
|
|||||||
return base::android::ScopedJavaLocalRef<jobject>(obj_);
|
return base::android::ScopedJavaLocalRef<jobject>(obj_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebContentsAndroid::CaptureContentAsBitmapForTesting(
|
|
||||||
JNIEnv* env,
|
|
||||||
const base::android::JavaParamRef<jobject>& jcallback) {
|
|
||||||
ui::GrabViewSnapshot(
|
|
||||||
web_contents_->GetNativeView(), gfx::Rect(web_contents_->GetSize()),
|
|
||||||
base::BindOnce(
|
|
||||||
&WebContentsAndroid::OnFinishGetContentBitmapForTesting,
|
|
||||||
weak_factory_.GetWeakPtr(),
|
|
||||||
base::android::ScopedJavaGlobalRef<jobject>(env, jcallback)));
|
|
||||||
}
|
|
||||||
|
|
||||||
void WebContentsAndroid::OnFinishGetContentBitmapForTesting(
|
|
||||||
const base::android::JavaRef<jobject>& callback,
|
|
||||||
gfx::Image snapshot) {
|
|
||||||
const SkBitmap bitmap = snapshot.AsBitmap();
|
|
||||||
CHECK(!bitmap.isNull());
|
|
||||||
CHECK(!bitmap.empty());
|
|
||||||
base::android::RunObjectCallbackAndroid(
|
|
||||||
callback,
|
|
||||||
gfx::ConvertToJavaBitmap(bitmap, gfx::OomBehavior::kReturnNullOnOom));
|
|
||||||
}
|
|
||||||
|
|
||||||
void WebContentsAndroid::ClearNativeReference(JNIEnv* env) {
|
void WebContentsAndroid::ClearNativeReference(JNIEnv* env) {
|
||||||
return web_contents_->ClearWebContentsAndroid();
|
return web_contents_->ClearWebContentsAndroid();
|
||||||
}
|
}
|
||||||
|
@ -235,13 +235,6 @@ class CONTENT_EXPORT WebContentsAndroid {
|
|||||||
void OnContentForNavigationEntryShown(JNIEnv* env);
|
void OnContentForNavigationEntryShown(JNIEnv* env);
|
||||||
jint GetCurrentBackForwardTransitionStage(JNIEnv* env);
|
jint GetCurrentBackForwardTransitionStage(JNIEnv* env);
|
||||||
|
|
||||||
void CaptureContentAsBitmapForTesting(
|
|
||||||
JNIEnv* env,
|
|
||||||
const base::android::JavaParamRef<jobject>& jcallback);
|
|
||||||
void OnFinishGetContentBitmapForTesting(
|
|
||||||
const base::android::JavaRef<jobject>& callback,
|
|
||||||
gfx::Image snapshot);
|
|
||||||
|
|
||||||
// Adds a crash report, like DumpWithoutCrashing(), including the Java stack
|
// Adds a crash report, like DumpWithoutCrashing(), including the Java stack
|
||||||
// trace from which `web_contents` was created. This is meant to help debug
|
// trace from which `web_contents` was created. This is meant to help debug
|
||||||
// cases where BrowserContext is destroyed before its WebContents.
|
// cases where BrowserContext is destroyed before its WebContents.
|
||||||
|
@ -25,7 +25,6 @@ import org.jni_zero.CalledByNative;
|
|||||||
import org.jni_zero.JNINamespace;
|
import org.jni_zero.JNINamespace;
|
||||||
import org.jni_zero.NativeMethods;
|
import org.jni_zero.NativeMethods;
|
||||||
|
|
||||||
import org.chromium.base.Callback;
|
|
||||||
import org.chromium.base.JavaExceptionReporter;
|
import org.chromium.base.JavaExceptionReporter;
|
||||||
import org.chromium.base.Log;
|
import org.chromium.base.Log;
|
||||||
import org.chromium.base.ObserverList;
|
import org.chromium.base.ObserverList;
|
||||||
@ -1249,16 +1248,9 @@ public class WebContentsImpl implements WebContents, RenderFrameHostDelegate, Wi
|
|||||||
"Native WebContents already destroyed", mNativeDestroyThrowable);
|
"Native WebContents already destroyed", mNativeDestroyThrowable);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void captureContentAsBitmapForTesting(Callback<Bitmap> callback) {
|
|
||||||
WebContentsImplJni.get()
|
|
||||||
.captureContentAsBitmapForTesting(mNativeWebContentsAndroid, callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
|
@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
|
||||||
@NativeMethods
|
@NativeMethods
|
||||||
public interface Natives {
|
public interface Natives {
|
||||||
|
|
||||||
// This is static to avoid exposing a public destroy method on the native side of this
|
// This is static to avoid exposing a public destroy method on the native side of this
|
||||||
// class.
|
// class.
|
||||||
void destroyWebContents(long webContentsAndroidPtr);
|
void destroyWebContents(long webContentsAndroidPtr);
|
||||||
@ -1462,8 +1454,5 @@ public class WebContentsImpl implements WebContents, RenderFrameHostDelegate, Wi
|
|||||||
long nativeWebContentsAndroid,
|
long nativeWebContentsAndroid,
|
||||||
BrowserControlsOffsetTagsInfo oldOffsetTagsInfo,
|
BrowserControlsOffsetTagsInfo oldOffsetTagsInfo,
|
||||||
BrowserControlsOffsetTagsInfo offsetTagsInfo);
|
BrowserControlsOffsetTagsInfo offsetTagsInfo);
|
||||||
|
|
||||||
void captureContentAsBitmapForTesting(
|
|
||||||
long nativeWebContentsAndroid, Callback<Bitmap> callback);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
|
|
||||||
package org.chromium.content_public.browser;
|
package org.chromium.content_public.browser;
|
||||||
|
|
||||||
import android.graphics.Bitmap;
|
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
@ -12,7 +11,6 @@ import android.os.Parcelable;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import org.chromium.base.Callback;
|
|
||||||
import org.chromium.blink_public.input.SelectionGranularity;
|
import org.chromium.blink_public.input.SelectionGranularity;
|
||||||
import org.chromium.cc.input.BrowserControlsOffsetTagsInfo;
|
import org.chromium.cc.input.BrowserControlsOffsetTagsInfo;
|
||||||
import org.chromium.content_public.browser.back_forward_transition.AnimationStage;
|
import org.chromium.content_public.browser.back_forward_transition.AnimationStage;
|
||||||
@ -623,6 +621,4 @@ public interface WebContents extends Parcelable {
|
|||||||
void notifyControlsConstraintsChanged(
|
void notifyControlsConstraintsChanged(
|
||||||
BrowserControlsOffsetTagsInfo oldOffsetTagsInfo,
|
BrowserControlsOffsetTagsInfo oldOffsetTagsInfo,
|
||||||
BrowserControlsOffsetTagsInfo offsetTagsInfo);
|
BrowserControlsOffsetTagsInfo offsetTagsInfo);
|
||||||
|
|
||||||
void captureContentAsBitmapForTesting(Callback<Bitmap> callback);
|
|
||||||
}
|
}
|
||||||
|
@ -5,14 +5,12 @@
|
|||||||
package org.chromium.content_public.browser.test.mock;
|
package org.chromium.content_public.browser.test.mock;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.graphics.Bitmap;
|
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import org.chromium.base.Callback;
|
|
||||||
import org.chromium.blink_public.input.SelectionGranularity;
|
import org.chromium.blink_public.input.SelectionGranularity;
|
||||||
import org.chromium.cc.input.BrowserControlsOffsetTagsInfo;
|
import org.chromium.cc.input.BrowserControlsOffsetTagsInfo;
|
||||||
import org.chromium.content_public.browser.GlobalRenderFrameHostId;
|
import org.chromium.content_public.browser.GlobalRenderFrameHostId;
|
||||||
@ -377,9 +375,6 @@ public class MockWebContents implements WebContents {
|
|||||||
return AnimationStage.NONE;
|
return AnimationStage.NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void captureContentAsBitmapForTesting(Callback<Bitmap> callback) {}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void notifyControlsConstraintsChanged(
|
public void notifyControlsConstraintsChanged(
|
||||||
BrowserControlsOffsetTagsInfo oldOffsetTagsInfo,
|
BrowserControlsOffsetTagsInfo oldOffsetTagsInfo,
|
||||||
|
Reference in New Issue
Block a user