1 // Copyright 2017 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 package org.chromium.android_webview.test; 6 7 import static org.chromium.android_webview.test.OnlyRunIn.ProcessMode.MULTI_PROCESS; 8 9 import android.util.Pair; 10 11 import androidx.test.filters.SmallTest; 12 13 import org.junit.After; 14 import org.junit.Assert; 15 import org.junit.Before; 16 import org.junit.Rule; 17 import org.junit.Test; 18 import org.junit.runner.RunWith; 19 20 import org.chromium.android_webview.AwContents; 21 import org.chromium.android_webview.AwRenderProcess; 22 import org.chromium.android_webview.AwRenderProcessGoneDetail; 23 import org.chromium.android_webview.renderer_priority.RendererPriority; 24 import org.chromium.base.task.PostTask; 25 import org.chromium.base.test.util.CallbackHelper; 26 import org.chromium.base.test.util.CommandLineFlags; 27 import org.chromium.base.test.util.DisabledTest; 28 import org.chromium.base.test.util.Feature; 29 import org.chromium.content_public.browser.UiThreadTaskTraits; 30 import org.chromium.content_public.browser.test.util.TestThreadUtils; 31 import org.chromium.content_public.common.ContentUrlConstants; 32 import org.chromium.net.test.util.TestWebServer; 33 34 import java.util.ArrayList; 35 import java.util.List; 36 import java.util.concurrent.TimeUnit; 37 38 /** 39 * Tests for AwContentsClient.onRenderProcessGone callback. 40 */ 41 @RunWith(AwJUnit4ClassRunner.class) 42 public class AwContentsClientOnRenderProcessGoneTest { 43 private static final String TAG = "AwRendererGone"; 44 @Rule 45 public AwActivityTestRule mActivityTestRule = new AwActivityTestRule(); 46 47 private TestWebServer mWebServer; 48 private RenderProcessGoneTestAwContentsClient mContentsClient; 49 private AwTestContainerView mTestView; 50 private AwContents mAwContents; 51 52 @Before setUp()53 public void setUp() throws Exception { 54 mWebServer = TestWebServer.start(); 55 mContentsClient = new RenderProcessGoneTestAwContentsClient(); 56 mTestView = mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient); 57 mAwContents = mTestView.getAwContents(); 58 } 59 60 @After tearDown()61 public void tearDown() { 62 mWebServer.shutdown(); 63 } 64 addPageToTestServer(String httpPath, String html)65 private String addPageToTestServer(String httpPath, String html) { 66 List<Pair<String, String>> headers = new ArrayList<Pair<String, String>>(); 67 headers.add(Pair.create("Content-Type", "text/html")); 68 headers.add(Pair.create("Cache-Control", "no-store")); 69 return mWebServer.setResponse(httpPath, html, headers); 70 } 71 72 private static class GetRenderProcessGoneHelper extends CallbackHelper { 73 private AwRenderProcessGoneDetail mDetail; 74 getAwRenderProcessGoneDetail()75 public AwRenderProcessGoneDetail getAwRenderProcessGoneDetail() { 76 assert getCallCount() > 0; 77 return mDetail; 78 } 79 notifyCalled(AwRenderProcessGoneDetail detail)80 public void notifyCalled(AwRenderProcessGoneDetail detail) { 81 mDetail = detail; 82 notifyCalled(); 83 } 84 } 85 86 private static class RenderProcessGoneTestAwContentsClient extends TestAwContentsClient { 87 88 private GetRenderProcessGoneHelper mGetRenderProcessGoneHelper; 89 RenderProcessGoneTestAwContentsClient()90 public RenderProcessGoneTestAwContentsClient() { 91 mGetRenderProcessGoneHelper = new GetRenderProcessGoneHelper(); 92 } 93 getGetRenderProcessGoneHelper()94 public GetRenderProcessGoneHelper getGetRenderProcessGoneHelper() { 95 return mGetRenderProcessGoneHelper; 96 } 97 98 @Override onRenderProcessGone(AwRenderProcessGoneDetail detail)99 public boolean onRenderProcessGone(AwRenderProcessGoneDetail detail) { 100 mGetRenderProcessGoneHelper.notifyCalled(detail); 101 return true; 102 } 103 } 104 105 interface Terminator { terminate()106 void terminate(); 107 } 108 createAndTerminateRenderProcess( Terminator terminator, boolean expectCrash)109 private AwRenderProcess createAndTerminateRenderProcess( 110 Terminator terminator, boolean expectCrash) throws Throwable { 111 GetRenderProcessGoneHelper helper = mContentsClient.getGetRenderProcessGoneHelper(); 112 113 final AwRenderProcess renderProcess = 114 TestThreadUtils.runOnUiThreadBlocking(() -> mAwContents.getRenderProcess()); 115 116 // Ensure that the renderer has started. 117 mActivityTestRule.loadUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(), 118 ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL); 119 120 // Terminate the renderer. 121 PostTask.runOrPostTask(UiThreadTaskTraits.DEFAULT, () -> terminator.terminate()); 122 123 // Assert that onRenderProcessGone is called once. 124 int callCount = helper.getCallCount(); 125 helper.waitForCallback(callCount, 1, CallbackHelper.WAIT_TIMEOUT_SECONDS * 5, 126 TimeUnit.SECONDS); 127 Assert.assertEquals(callCount + 1, helper.getCallCount()); 128 Assert.assertEquals(helper.getAwRenderProcessGoneDetail().didCrash(), expectCrash); 129 Assert.assertEquals( 130 RendererPriority.HIGH, helper.getAwRenderProcessGoneDetail().rendererPriority()); 131 132 return renderProcess; 133 } 134 135 @Test 136 @DisabledTest // http://crbug.com/689292 137 @Feature({"AndroidWebView"}) 138 @SmallTest 139 @OnlyRunIn(MULTI_PROCESS) testOnRenderProcessCrash()140 public void testOnRenderProcessCrash() throws Throwable { 141 createAndTerminateRenderProcess(() -> { mAwContents.loadUrl("chrome://crash"); }, true); 142 } 143 144 @Test 145 @Feature({"AndroidWebView"}) 146 @SmallTest 147 @OnlyRunIn(MULTI_PROCESS) testOnRenderProcessKill()148 public void testOnRenderProcessKill() throws Throwable { 149 createAndTerminateRenderProcess(() -> { mAwContents.loadUrl("chrome://kill"); }, false); 150 } 151 152 @Test 153 @Feature({"AndroidWebView"}) 154 @SmallTest 155 @OnlyRunIn(MULTI_PROCESS) testRenderProcessTermination()156 public void testRenderProcessTermination() throws Throwable { 157 createAndTerminateRenderProcess( 158 () -> { mAwContents.getRenderProcess().terminate(); }, false); 159 } 160 161 @Test 162 @Feature({"AndroidWebView"}) 163 @SmallTest 164 @OnlyRunIn(MULTI_PROCESS) testRenderProcessDifferentAfterRestart()165 public void testRenderProcessDifferentAfterRestart() throws Throwable { 166 AwRenderProcess renderProcess1 = createAndTerminateRenderProcess( 167 () -> { mAwContents.getRenderProcess().terminate(); }, false); 168 AwRenderProcess renderProcess2 = createAndTerminateRenderProcess( 169 () -> { mAwContents.getRenderProcess().terminate(); }, false); 170 Assert.assertNotEquals(renderProcess1, renderProcess2); 171 } 172 173 @Test 174 @Feature({"AndroidWebView"}) 175 @SmallTest 176 @OnlyRunIn(MULTI_PROCESS) testRenderProcessCanNotTerminateBeforeStart()177 public void testRenderProcessCanNotTerminateBeforeStart() throws Throwable { 178 Assert.assertFalse(TestThreadUtils.runOnUiThreadBlocking( 179 () -> mAwContents.getRenderProcess().terminate())); 180 } 181 182 @Test 183 @Feature({"AndroidWebView"}) 184 @SmallTest 185 @OnlyRunIn(MULTI_PROCESS) testRenderProcessSameBeforeAndAfterStart()186 public void testRenderProcessSameBeforeAndAfterStart() throws Throwable { 187 AwRenderProcess renderProcessBeforeStart = 188 TestThreadUtils.runOnUiThreadBlocking(() -> mAwContents.getRenderProcess()); 189 190 // Ensure that the renderer has started. 191 mActivityTestRule.loadUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(), 192 ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL); 193 194 AwRenderProcess renderProcessAfterStart = 195 TestThreadUtils.runOnUiThreadBlocking(() -> mAwContents.getRenderProcess()); 196 197 Assert.assertEquals(renderProcessBeforeStart, renderProcessAfterStart); 198 } 199 200 @Test 201 @Feature({"AndroidWebView"}) 202 @SmallTest 203 @OnlyRunIn(MULTI_PROCESS) 204 // The RenderDocument feature has a "level" parameter. This enables the feature and sets the 205 // default level to "crashed-frame" to enable replacing the render frame host of crashed frames 206 // with a new render frame host (instead of resuing the old one). See 207 // https://go/force-field-trials-docs for the syntax of these flags. 208 @CommandLineFlags.Add({ 209 "enable-features=RenderDocument<RenderDocument", 210 "force-fieldtrials=RenderDocument/Group1", 211 "force-fieldtrial-params=RenderDocument.Group1:level/crashed-frame", 212 }) 213 public void testNavigationAfterCrashAndJavaScript()214 testNavigationAfterCrashAndJavaScript() throws Throwable { 215 // In https://crbug.com/1006814, a crashed frame, reinitialized by running JS fails to 216 // navigate. 217 mActivityTestRule.getAwSettingsOnUiThread(mAwContents).setJavaScriptEnabled(true); 218 // Crash the frame. 219 AwRenderProcess renderProcess1 = createAndTerminateRenderProcess( 220 () -> { mAwContents.getRenderProcess().terminate(); }, false); 221 // Run JS in the frame. 222 Assert.assertEquals("3", 223 mActivityTestRule.executeJavaScriptAndWaitForResult( 224 mAwContents, mContentsClient, "1+2")); 225 // Navigate to somewhere else. This should not crash the frame. 226 final String content = "some content"; 227 final String httpPathOnServer = addPageToTestServer("/page.html", content); 228 mActivityTestRule.loadUrlSync( 229 mAwContents, mContentsClient.getOnPageFinishedHelper(), httpPathOnServer); 230 // Result is a string, so needs "". 231 Assert.assertEquals("\"" + content + "\"", 232 mActivityTestRule.executeJavaScriptAndWaitForResult( 233 mAwContents, mContentsClient, "document.body.textContent")); 234 } 235 } 236