Commit 4d9776c1 authored by piotrs's avatar piotrs Committed by Commit Bot

Redirects _blank and window.open() off-origin navigation from PWA to CCT.

Custom Tab opened from a _blank link or a window.open() call is opened in
a separate Android task to mimic a new window/blank context of a web browser.
This is done in order to improve UX and bring more app-like feel to PWAs.
Transition to CCT for regular navigation is already done
(codereview.chromium.org/2829943002), this patch brings the same to _blank
and window.open().

This requires some changes to Custom Tabs, as WebContents for the new window
is already created when we get to Java layer. I replicated the trick from
ChromeTabCreator with remembering WebContents on AsyncTabParamsManager
during the transition.

Tests asserting that _blank and window.open() are opened in TabbedChrome has
been removed from WebappModeTest. Tests for transition to CCT has been added
to WebappNavigationTest.

On Lollipop and above we maintain at most one CCT task per installed Webapp.
Below Lollipop there is no API I could use to enforce that I think, let me
know if you know the way.

BUG=709889

Review-Url: https://codereview.chromium.org/2898373002
Cr-Commit-Position: refs/heads/master@{#480286}
parent 9c5eb06c
......@@ -65,6 +65,7 @@ import org.chromium.chrome.browser.rappor.RapporServiceBridge;
import org.chromium.chrome.browser.tab.EmptyTabObserver;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tab.TabDelegateFactory;
import org.chromium.chrome.browser.tabmodel.AsyncTabParams;
import org.chromium.chrome.browser.tabmodel.AsyncTabParamsManager;
import org.chromium.chrome.browser.tabmodel.ChromeTabCreator;
import org.chromium.chrome.browser.tabmodel.EmptyTabModelObserver;
......@@ -98,7 +99,8 @@ public class CustomTabActivity extends ChromeActivity {
private static final int WEBCONTENTS_STATE_NO_WEBCONTENTS = 0;
private static final int WEBCONTENTS_STATE_PRERENDERED_WEBCONTENTS = 1;
private static final int WEBCONTENTS_STATE_SPARE_WEBCONTENTS = 2;
private static final int WEBCONTENTS_STATE_MAX = 3;
private static final int WEBCONTENTS_STATE_TRANSFERRED_WEBCONTENTS = 3;
private static final int WEBCONTENTS_STATE_MAX = 4;
private static CustomTabContentHandler sActiveContentHandler;
......@@ -574,28 +576,15 @@ public class CustomTabActivity extends ChromeActivity {
private Tab createMainTab() {
CustomTabsConnection connection = CustomTabsConnection.getInstance(getApplication());
String url = getUrlToLoad();
String referrerUrl = connection.getReferrer(mSession, getIntent());
Tab tab = new Tab(Tab.INVALID_TAB_ID, Tab.INVALID_TAB_ID, false, this, getWindowAndroid(),
WebContents webContents = takeWebContents(connection);
int assignedTabId = IntentUtils.safeGetIntExtra(
getIntent(), IntentHandler.EXTRA_TAB_ID, Tab.INVALID_TAB_ID);
int parentTabId = IntentUtils.safeGetIntExtra(
getIntent(), IntentHandler.EXTRA_PARENT_TAB_ID, Tab.INVALID_TAB_ID);
Tab tab = new Tab(assignedTabId, parentTabId, false, this, getWindowAndroid(),
TabLaunchType.FROM_EXTERNAL_APP, null, null);
tab.setAppAssociatedWith(connection.getClientPackageNameForSession(mSession));
int webContentsStateOnLaunch = WEBCONTENTS_STATE_NO_WEBCONTENTS;
WebContents webContents = connection.takePrerenderedUrl(mSession, url, referrerUrl);
mUsingPrerender = webContents != null;
if (mUsingPrerender) webContentsStateOnLaunch = WEBCONTENTS_STATE_PRERENDERED_WEBCONTENTS;
if (!mUsingPrerender) {
webContents = WarmupManager.getInstance().takeSpareWebContents(false, false);
if (webContents != null) webContentsStateOnLaunch = WEBCONTENTS_STATE_SPARE_WEBCONTENTS;
}
RecordHistogram.recordEnumeratedHistogram("CustomTabs.WebContentsStateOnLaunch",
webContentsStateOnLaunch, WEBCONTENTS_STATE_MAX);
if (webContents == null) {
webContents = WebContentsFactory.createWebContentsWithWarmRenderer(false, false);
}
if (!mUsingPrerender) {
connection.resetPostMessageHandlerForSession(mSession, webContents);
}
tab.initialize(
webContents, getTabContentManager(),
new CustomTabDelegateFactory(
......@@ -612,6 +601,52 @@ public class CustomTabActivity extends ChromeActivity {
return tab;
}
private WebContents takeWebContents(CustomTabsConnection connection) {
mUsingPrerender = true;
int webContentsStateOnLaunch = WEBCONTENTS_STATE_PRERENDERED_WEBCONTENTS;
WebContents webContents = takePrerenderedWebContents(connection);
if (webContents == null) {
mUsingPrerender = false;
webContents = takeAsyncWebContents();
if (webContents != null) {
webContentsStateOnLaunch = WEBCONTENTS_STATE_TRANSFERRED_WEBCONTENTS;
} else {
webContents = WarmupManager.getInstance().takeSpareWebContents(false, false);
if (webContents != null) {
webContentsStateOnLaunch = WEBCONTENTS_STATE_SPARE_WEBCONTENTS;
} else {
webContents =
WebContentsFactory.createWebContentsWithWarmRenderer(false, false);
webContentsStateOnLaunch = WEBCONTENTS_STATE_NO_WEBCONTENTS;
}
}
}
RecordHistogram.recordEnumeratedHistogram("CustomTabs.WebContentsStateOnLaunch",
webContentsStateOnLaunch, WEBCONTENTS_STATE_MAX);
if (!mUsingPrerender) {
connection.resetPostMessageHandlerForSession(mSession, webContents);
}
return webContents;
}
private WebContents takePrerenderedWebContents(CustomTabsConnection connection) {
String url = getUrlToLoad();
String referrerUrl = connection.getReferrer(mSession, getIntent());
return connection.takePrerenderedUrl(mSession, url, referrerUrl);
}
private WebContents takeAsyncWebContents() {
int assignedTabId = IntentUtils.safeGetIntExtra(
getIntent(), IntentHandler.EXTRA_TAB_ID, Tab.INVALID_TAB_ID);
AsyncTabParams asyncParams = AsyncTabParamsManager.remove(assignedTabId);
if (asyncParams == null) return null;
return asyncParams.getWebContents();
}
private void initializeMainTab(Tab tab) {
tab.getTabRedirectHandler().updateIntent(getIntent());
tab.getView().requestFocus();
......
......@@ -140,6 +140,13 @@ public class TabDelegate extends TabCreator {
intent.setComponent(componentName);
}
addAsyncTabExtras(asyncParams, parentId, isChromeUI, assignedTabId, intent);
return intent;
}
protected final void addAsyncTabExtras(AsyncTabCreationParams asyncParams, int parentId,
boolean isChromeUI, int assignedTabId, Intent intent) {
Map<String, String> extraHeaders = asyncParams.getLoadUrlParams().getExtraHeaders();
if (extraHeaders != null && !extraHeaders.isEmpty()) {
Bundle bundle = new Bundle();
......@@ -171,7 +178,6 @@ public class TabDelegate extends TabCreator {
}
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
return intent;
}
/**
......
......@@ -39,6 +39,7 @@ import org.chromium.chrome.browser.tab.EmptyTabObserver;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tab.TabDelegateFactory;
import org.chromium.chrome.browser.tab.TabObserver;
import org.chromium.chrome.browser.tabmodel.document.TabDelegate;
import org.chromium.chrome.browser.util.ColorUtils;
import org.chromium.chrome.browser.util.UrlUtilities;
import org.chromium.content.browser.ScreenOrientationProvider;
......@@ -714,6 +715,11 @@ public class WebappActivity extends FullScreenActivity {
return new WebappDelegateFactory(this);
}
@Override
protected TabDelegate createTabDelegate(boolean incognito) {
return new WebappTabDelegate(this, incognito);
}
// We're temporarily disable CS on webapp since there are some issues. (http://crbug.com/471950)
// TODO(changwan): re-enable it once the issues are resolved.
@Override
......
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.webapps;
import android.net.Uri;
import android.support.customtabs.CustomTabsIntent;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tab.TabIdManager;
import org.chromium.chrome.browser.tabmodel.AsyncTabParamsManager;
import org.chromium.chrome.browser.tabmodel.TabModel.TabLaunchType;
import org.chromium.chrome.browser.tabmodel.document.AsyncTabCreationParams;
import org.chromium.chrome.browser.tabmodel.document.TabDelegate;
/**
* Asynchronously creates Tabs for navigation originating from an installed PWA.
*
* This is the same as the parent class with exception of opening a Custom Tab for
* {@code _blank} links and {@code window.open(url)} calls instead of creating a new tab in Chrome.
*/
public class WebappTabDelegate extends TabDelegate {
private final WebappActivity mActivity;
public WebappTabDelegate(WebappActivity activity, boolean incognito) {
super(incognito);
this.mActivity = activity;
}
@Override
public void createNewTab(AsyncTabCreationParams asyncParams, TabLaunchType type, int parentId) {
int assignedTabId = TabIdManager.getInstance().generateValidId(Tab.INVALID_TAB_ID);
AsyncTabParamsManager.add(assignedTabId, asyncParams);
CustomTabsIntent customTabIntent =
new CustomTabsIntent.Builder().setShowTitle(true).build();
customTabIntent.intent.setPackage(mActivity.getPackageName());
addAsyncTabExtras(asyncParams, parentId, true, assignedTabId, customTabIntent.intent);
customTabIntent.launchUrl(mActivity, Uri.parse(asyncParams.getLoadUrlParams().getUrl()));
}
}
......@@ -1212,6 +1212,7 @@ chrome_java_sources = [
"java/src/org/chromium/chrome/browser/webapps/WebappLauncherActivity.java",
"java/src/org/chromium/chrome/browser/webapps/WebappManagedActivity.java",
"java/src/org/chromium/chrome/browser/webapps/WebappRegistry.java",
"java/src/org/chromium/chrome/browser/webapps/WebappTabDelegate.java",
"java/src/org/chromium/chrome/browser/webapps/WebappUrlBar.java",
"java/src/org/chromium/chrome/browser/webshare/ShareServiceImpl.java",
"java/src/org/chromium/chrome/browser/webshare/ShareServiceImplementationFactory.java",
......
......@@ -21,7 +21,6 @@ import org.junit.runner.RunWith;
import org.chromium.base.ApplicationStatus;
import org.chromium.base.ContextUtils;
import org.chromium.base.ThreadUtils;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.base.test.util.Feature;
import org.chromium.base.test.util.RetryOnFailure;
......@@ -36,13 +35,9 @@ import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tab.TabIdManager;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.chrome.test.MultiActivityTestRule;
import org.chromium.chrome.test.util.ActivityUtils;
import org.chromium.chrome.test.util.ApplicationTestUtils;
import org.chromium.chrome.test.util.browser.TabLoadObserver;
import org.chromium.content.browser.test.util.Criteria;
import org.chromium.content.browser.test.util.CriteriaHelper;
import org.chromium.content.browser.test.util.JavaScriptUtils;
import org.chromium.content.browser.test.util.TouchCommon;
import org.chromium.content_public.common.ScreenOrientationValues;
import org.chromium.net.test.EmbeddedTestServer;
......@@ -272,77 +267,6 @@ public class WebappModeTest {
});
}
/**
* Tests that WebappActivities handle window.open() properly in tabbed mode.
*/
@Test
@MediumTest
@Feature({"Webapps"})
public void testWebappHandlesWindowOpenInTabbedMode() throws Exception {
triggerWindowOpenAndWaitForLoad(ChromeTabbedActivity.class, getOnClickLinkUrl(), true);
}
/**
* Tests that WebappActivities handle suppressed window.open() properly in tabbed mode.
*/
@Test
@MediumTest
@Feature({"Webapps"})
public void testWebappHandlesSuppressedWindowOpenInTabbedMode() throws Exception {
triggerWindowOpenAndWaitForLoad(
ChromeTabbedActivity.class, getHrefNoReferrerLinkUrl(), false);
}
private <T extends ChromeActivity> void triggerWindowOpenAndWaitForLoad(
Class<T> classToWaitFor, String linkHtml, boolean checkContents) throws Exception {
final WebappActivity firstActivity =
startWebappActivity(WEBAPP_1_ID, WEBAPP_1_URL, WEBAPP_1_TITLE, WEBAPP_ICON);
final int firstWebappId = firstActivity.getActivityTab().getId();
// Load up the test page.
new TabLoadObserver(firstActivity.getActivityTab()).fullyLoadUrl(linkHtml);
// Do a plain click to make the link open in the main browser via a window.open().
// If the window is opened successfully, javascript on the first page triggers and changes
// its URL as a signal for this test.
Runnable fgTrigger = new Runnable() {
@Override
public void run() {
ThreadUtils.runOnUiThreadBlocking(new Runnable() {
@Override
public void run() {
View view = firstActivity.findViewById(android.R.id.content).getRootView();
TouchCommon.singleClickView(view);
}
});
}
};
ChromeActivity secondActivity = ActivityUtils.waitForActivity(
InstrumentationRegistry.getInstrumentation(), classToWaitFor, fgTrigger);
mTestRule.waitForFullLoad(secondActivity, "The Google");
if (checkContents) {
Assert.assertEquals("New WebContents was not created", "SUCCESS",
firstActivity.getActivityTab().getTitle());
}
Assert.assertNotSame("Wrong Activity in foreground", firstActivity,
ApplicationStatus.getLastTrackedFocusedActivity());
// Close the child window to kick the user back to the WebappActivity.
JavaScriptUtils.executeJavaScript(
secondActivity.getActivityTab().getWebContents(), "window.close()");
CriteriaHelper.pollUiThread(new Criteria() {
@Override
public boolean isSatisfied() {
Activity lastActivity = ApplicationStatus.getLastTrackedFocusedActivity();
if (!isWebappActivityReady(lastActivity)) return false;
WebappActivity webappActivity = (WebappActivity) lastActivity;
return webappActivity.getActivityTab().getId() == firstWebappId;
}
});
ApplicationTestUtils.waitUntilChromeInForeground();
}
/**
* Starts a WebappActivity for the given data and waits for it to be initialized. We can't use
* ActivityUtils.waitForActivity() because of the way WebappActivity is instanced on pre-L
......@@ -372,49 +296,4 @@ public class WebappModeTest {
return true;
}
/** Defines one gigantic link spanning the whole page that creates a new
* window with chrome/test/data/android/google.html. Disallowing a referrer from being
* sent triggers another codepath.
*/
private String getHrefNoReferrerLinkUrl() {
return UrlUtils.encodeHtmlDataUri("<html>"
+ " <head>"
+ " <title>href no referrer link page</title>"
+ " <meta name='viewport'"
+ " content='width=device-width initial-scale=0.5, maximum-scale=0.5'>"
+ " <style>"
+ " body {margin: 0em;} div {width: 100%; height: 100%; background: #011684;}"
+ " </style>"
+ " </head>"
+ " <body>"
+ " <a href='" + mTestServer.getURL("/chrome/test/data/android/google.html")
+ "' target='_blank' rel='noreferrer'><div></div></a>"
+ " </body>");
}
/** Returns a URL where clicking the body triggers a window.open() call to open
* chrome/test/data/android/google.html. */
private String getOnClickLinkUrl() {
return UrlUtils.encodeHtmlDataUri("<html>"
+ " <head>"
+ " <title>window.open page</title>"
+ " <meta name='viewport'"
+ " content='width=device-width initial-scale=0.5, maximum-scale=0.5'>"
+ " <style>"
+ " body {margin: 0em;} div {width: 100%; height: 100%; background: #011684;}"
+ " </style>"
+ " <script>"
+ " function openNewWindow() {"
+ " var site = window.open('"
+ mTestServer.getURL("/chrome/test/data/android/google.html") + "');"
+ " document.title = site ? 'SUCCESS' : 'FAILURE';"
+ " }"
+ " </script>"
+ " </head>"
+ " <body id='body'>"
+ " <div onclick='openNewWindow()'></div>"
+ " </body>"
+ "</html>");
}
}
......@@ -4,8 +4,12 @@
package org.chromium.chrome.browser.webapps;
import android.app.ActivityManager;
import android.app.ActivityManager.AppTask;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.os.Build;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
......@@ -17,7 +21,6 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.chromium.base.ApiCompatibilityUtils;
import org.chromium.base.ThreadUtils;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.base.test.util.Feature;
import org.chromium.chrome.R;
......@@ -28,7 +31,7 @@ import org.chromium.chrome.browser.customtabs.CustomTabActivity;
import org.chromium.chrome.browser.firstrun.FirstRunStatus;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.chrome.test.util.browser.contextmenu.ContextMenuUtils;
import org.chromium.content_public.browser.LoadUrlParams;
import org.chromium.content.browser.test.util.DOMUtils;
import org.chromium.net.test.EmbeddedTestServer;
import org.chromium.ui.base.PageTransition;
......@@ -64,18 +67,12 @@ public class WebappNavigationTest {
@Test
@SmallTest
@Feature({"Webapps"})
public void testOffOriginNavigationUsingLinkAndNoWebappThemeColor() throws Exception {
public void testRegularLinkOffOriginInCctNoWebappThemeColor() throws Exception {
runWebappActivityAndWaitForIdle(mActivityTestRule.createIntent());
// Not using #loadUrl, as it expects the URL to load in the activity under test,
// which is not happening here.
ThreadUtils.runOnUiThreadBlocking(new Runnable() {
@Override
public void run() {
mActivityTestRule.getActivity().getActivityTab().loadUrl(
new LoadUrlParams(OFF_ORIGIN_URL, PageTransition.LINK));
}
});
assertNumberOfTasksInRecents("We should start with one Android task", 1);
addAnchor("testId", OFF_ORIGIN_URL, "_self");
DOMUtils.clickNode(
mActivityTestRule.getActivity().getActivityTab().getContentViewCore(), "testId");
CustomTabActivity customTab = assertCustomTabActivityLaunchedForOffOriginUrl();
......@@ -84,14 +81,17 @@ public class WebappNavigationTest {
ApiCompatibilityUtils.getColor(
customTab.getResources(), R.color.default_primary_color),
customTab.getToolbarManager().getPrimaryColor());
assertNumberOfTasksInRecents(
"Link with target=\"_self\" should stay in the same Android task.", 1);
}
@Test
@SmallTest
@Feature({"Webapps"})
public void testOffOriginNavigationUsingJavaScriptAndWebappThemeColor() throws Exception {
public void testWindowTopLocationOffOriginInCctAndWebappThemeColor() throws Exception {
runWebappActivityAndWaitForIdle(mActivityTestRule.createIntent().putExtra(
ShortcutHelper.EXTRA_THEME_COLOR, (long) Color.CYAN));
assertNumberOfTasksInRecents("We should start with one Android task", 1);
mActivityTestRule.runJavaScriptCodeInCurrentTab(
String.format("window.top.location = '%s'", OFF_ORIGIN_URL));
......@@ -100,16 +100,58 @@ public class WebappNavigationTest {
Assert.assertEquals("CCT Toolbar should use the theme color of a webapp", Color.CYAN,
customTab.getToolbarManager().getPrimaryColor());
assertNumberOfTasksInRecents(
"Executing window.top.location = url; should stay in the same Android task.", 1);
}
private CustomTabActivity assertCustomTabActivityLaunchedForOffOriginUrl() {
CustomTabActivity customTab = activityListener.waitFor(CustomTabActivity.class);
@Test
@SmallTest
@Feature({"Webapps"})
public void testNewTabLinkOpensInCct() throws Exception {
runWebappActivityAndWaitForIdle(mActivityTestRule.createIntent().putExtra(
ShortcutHelper.EXTRA_THEME_COLOR, (long) Color.CYAN));
assertNumberOfTasksInRecents("We should start with one Android task", 1);
addAnchor("testId", OFF_ORIGIN_URL, "_blank");
DOMUtils.clickNode(
mActivityTestRule.getActivity().getActivityTab().getContentViewCore(), "testId");
CustomTabActivity customTab = assertCustomTabActivityLaunchedForOffOriginUrl();
Assert.assertEquals(
"CCT Toolbar should use default primary color even if webapp has theme color",
ApiCompatibilityUtils.getColor(
customTab.getResources(), R.color.default_primary_color),
customTab.getToolbarManager().getPrimaryColor());
assertNumberOfTasksInRecents(
"Link with target=\"_blank\" should open in a new Android task.", 2);
}
mActivityTestRule.waitUntilIdle(customTab);
// Dropping the TLD as Google can redirect to a local site, so this could fail outside US.
Assert.assertTrue(customTab.getActivityTab().getUrl().startsWith("https://www.google."));
@Test
@SmallTest
@Feature({"Webapps"})
public void testWindowOpenInCct() throws Exception {
runWebappActivityAndWaitForIdle(mActivityTestRule.createIntent());
assertNumberOfTasksInRecents("We should start with one Android task", 1);
// Executing window.open() through a click on a link,
// as it needs user gesture to avoid Chrome blocking it as a popup.
mActivityTestRule.runJavaScriptCodeInCurrentTab(
String.format("var aTag = document.createElement('testId');"
+ "aTag.id = 'testId';"
+ "aTag.innerHTML = 'Click Me!';"
+ "aTag.onclick = function() {"
+ " window.open('%s');"
+ " return false;"
+ "};"
+ "document.body.appendChild(aTag);",
OFF_ORIGIN_URL));
DOMUtils.clickNode(
mActivityTestRule.getActivity().getActivityTab().getContentViewCore(), "testId");
return customTab;
CustomTabActivity customTab = assertCustomTabActivityLaunchedForOffOriginUrl();
Assert.assertEquals("CCT Toolbar should use default primary color",
ApiCompatibilityUtils.getColor(
customTab.getResources(), R.color.default_primary_color),
customTab.getToolbarManager().getPrimaryColor());
assertNumberOfTasksInRecents(
"Executing window.open(url) should open a new Android task.", 2);
}
@Test
......@@ -138,11 +180,7 @@ public class WebappNavigationTest {
FirstRunStatus.setFirstRunFlowComplete(true);
runWebappActivityAndWaitForIdle(mActivityTestRule.createIntent());
mActivityTestRule.runJavaScriptCodeInCurrentTab("var aTag = document.createElement('a');"
+ "aTag.id = 'myTestAnchorId';"
+ "aTag.setAttribute('href','https://www.google.com/');"
+ "aTag.innerHTML = 'Click Me!';"
+ "document.body.appendChild(aTag);");
addAnchor("myTestAnchorId", OFF_ORIGIN_URL, "_self");
ContextMenuUtils.selectContextMenuItem(InstrumentationRegistry.getInstrumentation(),
null /* activity to check for focus after click */,
......@@ -163,4 +201,45 @@ public class WebappNavigationTest {
mActivityTestRule.waitUntilSplashscreenHides();
mActivityTestRule.waitUntilIdle();
}
private CustomTabActivity assertCustomTabActivityLaunchedForOffOriginUrl() {
CustomTabActivity customTab = activityListener.waitFor(CustomTabActivity.class);
mActivityTestRule.waitUntilIdle(customTab);
// Dropping the TLD as Google can redirect to a local site, so this could fail outside US.
Assert.assertTrue(customTab.getActivityTab().getUrl().contains("https://www.google."));
return customTab;
}
private void addAnchor(String id, String url, String target) throws Exception {
mActivityTestRule.runJavaScriptCodeInCurrentTab(
String.format("var aTag = document.createElement('a');"
+ "aTag.id = '%s';"
+ "aTag.setAttribute('href','%s');"
+ "aTag.setAttribute('target','%s');"
+ "aTag.innerHTML = 'Click Me!';"
+ "document.body.appendChild(aTag);",
id, url, target));
}
private void assertNumberOfTasksInRecents(String message, int expectedNumberOfTasks) {
// We only have API to check this since Lollipop.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return;
ActivityManager activityManager =
(ActivityManager) mActivityTestRule.getActivity().getSystemService(
Context.ACTIVITY_SERVICE);
int count = 0;
for (AppTask task : activityManager.getAppTasks()) {
if ((task.getTaskInfo().baseIntent.getFlags()
& Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
== 0) {
count++;
}
}
Assert.assertEquals(message, expectedNumberOfTasks, count);
}
}
......@@ -37856,6 +37856,7 @@ from previous Chrome versions.
<int value="0" label="No WebContents"/>
<int value="1" label="Prerendered WebContents"/>
<int value="2" label="Spare WebContents"/>
<int value="3" label="Transferred WebContents"/>
</enum>
<enum name="WebFontCacheHit">
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment