diff --git a/components/prerender/browser/prerender_contents.cc b/components/prerender/browser/prerender_contents.cc
index 93cd80fa82c1c5..a1d64cda2983f0 100644
--- a/components/prerender/browser/prerender_contents.cc
+++ b/components/prerender/browser/prerender_contents.cc
@@ -190,7 +190,8 @@ void PrerenderContents::StartPrerendering(
DCHECK(!prerender_contents_);
DCHECK_EQ(1U, alias_urls_.size());
- session_storage_namespace_id_ = session_storage_namespace->id();
+ if (session_storage_namespace)
+ session_storage_namespace_id_ = session_storage_namespace->id();
bounds_ = bounds;
DCHECK(load_start_time_.is_null());
diff --git a/weblayer/BUILD.gn b/weblayer/BUILD.gn
index be8d517723f953..d6a0145554e9fe 100644
--- a/weblayer/BUILD.gn
+++ b/weblayer/BUILD.gn
@@ -209,6 +209,8 @@ source_set("weblayer_lib_base") {
"browser/navigation_impl.h",
"browser/navigation_ui_data_impl.cc",
"browser/navigation_ui_data_impl.h",
+ "browser/no_state_prefetch/prerender_controller_impl.cc",
+ "browser/no_state_prefetch/prerender_controller_impl.h",
"browser/no_state_prefetch/prerender_link_manager_factory.cc",
"browser/no_state_prefetch/prerender_link_manager_factory.h",
"browser/no_state_prefetch/prerender_manager_delegate_impl.cc",
@@ -325,6 +327,7 @@ source_set("weblayer_lib_base") {
"public/navigation_controller.h",
"public/navigation_observer.h",
"public/new_tab_delegate.h",
+ "public/prerender_controller.h",
"public/profile.h",
"public/tab.h",
"public/tab_observer.h",
diff --git a/weblayer/browser/android/javatests/BUILD.gn b/weblayer/browser/android/javatests/BUILD.gn
index 862d5bbc71f638..2d4b6a797e66e9 100644
--- a/weblayer/browser/android/javatests/BUILD.gn
+++ b/weblayer/browser/android/javatests/BUILD.gn
@@ -29,6 +29,7 @@ android_library("weblayer_java_tests") {
"src/org/chromium/weblayer/test/NavigationTest.java",
"src/org/chromium/weblayer/test/NewTabCallbackTest.java",
"src/org/chromium/weblayer/test/OnTabRemovedTabListCallbackImpl.java",
+ "src/org/chromium/weblayer/test/PrerenderControllerTest.java",
"src/org/chromium/weblayer/test/ProfileTest.java",
"src/org/chromium/weblayer/test/RenderingTest.java",
"src/org/chromium/weblayer/test/ScrollOffsetCallbackTest.java",
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/PrerenderControllerTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/PrerenderControllerTest.java
new file mode 100644
index 00000000000000..3d4c005de6919d
--- /dev/null
+++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/PrerenderControllerTest.java
@@ -0,0 +1,69 @@
+// Copyright 2020 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.weblayer.test;
+
+import android.net.Uri;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.test.util.CallbackHelper;
+import org.chromium.base.test.util.Feature;
+import org.chromium.content_public.browser.test.util.TestThreadUtils;
+import org.chromium.net.test.util.TestWebServer;
+import org.chromium.net.test.util.WebServer;
+import org.chromium.weblayer.PrerenderController;
+import org.chromium.weblayer.shell.InstrumentationActivity;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/** Tests verifying PrerenderController behavior. */
+@RunWith(WebLayerJUnit4ClassRunner.class)
+public class PrerenderControllerTest {
+ private static final String DEFAULT_BODY = "
TestPageHello World!";
+ private CallbackHelper mPrerenderedPageFetched;
+
+ private WebServer.RequestHandler mRequestHandler = new WebServer.RequestHandler() {
+ @Override
+ public void handleRequest(WebServer.HTTPRequest request, OutputStream stream) {
+ try {
+ if (request.getURI().contains("prerendered_page.html")) {
+ TestThreadUtils.runOnUiThreadBlocking(
+ () -> mPrerenderedPageFetched.notifyCalled());
+ }
+ WebServer.writeResponse(stream, WebServer.STATUS_OK, DEFAULT_BODY.getBytes());
+ } catch (IOException exception) {
+ Assert.fail(exception.getMessage()
+ + " \n while handling request: " + request.toString());
+ }
+ }
+ };
+
+ @Rule
+ public InstrumentationActivityTestRule mActivityTestRule =
+ new InstrumentationActivityTestRule();
+
+ @Test
+ @SmallTest
+ @Feature({"WebLayer"})
+ public void testAddingPrerender() throws Exception {
+ TestWebServer testServer = TestWebServer.start();
+ testServer.setRequestHandler(mRequestHandler);
+ mPrerenderedPageFetched = new CallbackHelper();
+ InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(null);
+ TestThreadUtils.runOnUiThreadBlocking(() -> {
+ PrerenderController prerenderController =
+ activity.getBrowser().getProfile().getPrerenderController();
+ prerenderController.schedulePrerender(
+ Uri.parse(testServer.getResponseUrl("/prerendered_page.html")));
+ });
+ mPrerenderedPageFetched.waitForFirst();
+ }
+}
diff --git a/weblayer/browser/java/BUILD.gn b/weblayer/browser/java/BUILD.gn
index 9b135686d9077c..d4bad3c94d9854 100644
--- a/weblayer/browser/java/BUILD.gn
+++ b/weblayer/browser/java/BUILD.gn
@@ -129,6 +129,7 @@ android_library("java") {
"org/chromium/weblayer_private/NavigationImpl.java",
"org/chromium/weblayer_private/NewTabCallbackProxy.java",
"org/chromium/weblayer_private/PageInfoControllerDelegateImpl.java",
+ "org/chromium/weblayer_private/PrerenderControllerImpl.java",
"org/chromium/weblayer_private/ProfileImpl.java",
"org/chromium/weblayer_private/ProfileManager.java",
"org/chromium/weblayer_private/RemoteFragmentImpl.java",
@@ -331,6 +332,7 @@ generate_jni("jni") {
"org/chromium/weblayer_private/NavigationControllerImpl.java",
"org/chromium/weblayer_private/NavigationImpl.java",
"org/chromium/weblayer_private/NewTabCallbackProxy.java",
+ "org/chromium/weblayer_private/PrerenderControllerImpl.java",
"org/chromium/weblayer_private/ProfileImpl.java",
"org/chromium/weblayer_private/TabCallbackProxy.java",
"org/chromium/weblayer_private/TabImpl.java",
@@ -432,6 +434,7 @@ android_aidl("aidl") {
"org/chromium/weblayer_private/interfaces/INavigationController.aidl",
"org/chromium/weblayer_private/interfaces/INavigationControllerClient.aidl",
"org/chromium/weblayer_private/interfaces/IObjectWrapper.aidl",
+ "org/chromium/weblayer_private/interfaces/IPrerenderController.aidl",
"org/chromium/weblayer_private/interfaces/IProfile.aidl",
"org/chromium/weblayer_private/interfaces/IRemoteFragment.aidl",
"org/chromium/weblayer_private/interfaces/IRemoteFragmentClient.aidl",
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/PrerenderControllerImpl.java b/weblayer/browser/java/org/chromium/weblayer_private/PrerenderControllerImpl.java
new file mode 100644
index 00000000000000..1f8089e54b88ec
--- /dev/null
+++ b/weblayer/browser/java/org/chromium/weblayer_private/PrerenderControllerImpl.java
@@ -0,0 +1,41 @@
+// Copyright 2020 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.weblayer_private;
+
+import org.chromium.base.LifetimeAssert;
+import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.NativeMethods;
+import org.chromium.weblayer_private.interfaces.IPrerenderController;
+
+/**
+ * Implementation of {@link IPrerenderController}.
+ */
+@JNINamespace("weblayer")
+public class PrerenderControllerImpl extends IPrerenderController.Stub {
+ private long mNativePrerenderController;
+ private final LifetimeAssert mLifetimeAssert = LifetimeAssert.create(this);
+
+ void destroy() {
+ mNativePrerenderController = 0;
+
+ // If mLifetimeAssert is GC'ed before this is called, it will throw an exception
+ // with a stack trace showing the stack during LifetimeAssert.create().
+ LifetimeAssert.setSafeToGc(mLifetimeAssert, true);
+ }
+
+ public PrerenderControllerImpl(long nativePrerenderController) {
+ mNativePrerenderController = nativePrerenderController;
+ }
+
+ @Override
+ public void prerender(String url) {
+ PrerenderControllerImplJni.get().prerender(mNativePrerenderController, url);
+ }
+
+ @NativeMethods()
+ interface Natives {
+ void prerender(long nativePrerenderControllerImpl, String url);
+ }
+}
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/ProfileImpl.java b/weblayer/browser/java/org/chromium/weblayer_private/ProfileImpl.java
index ee752c94eff015..d29d569d0ef769 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/ProfileImpl.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/ProfileImpl.java
@@ -21,6 +21,7 @@
import org.chromium.weblayer_private.interfaces.ICookieManager;
import org.chromium.weblayer_private.interfaces.IDownloadCallbackClient;
import org.chromium.weblayer_private.interfaces.IObjectWrapper;
+import org.chromium.weblayer_private.interfaces.IPrerenderController;
import org.chromium.weblayer_private.interfaces.IProfile;
import org.chromium.weblayer_private.interfaces.IUserIdentityCallbackClient;
import org.chromium.weblayer_private.interfaces.ObjectWrapper;
@@ -41,6 +42,7 @@ public final class ProfileImpl extends IProfile.Stub implements BrowserContextHa
private final String mName;
private long mNativeProfile;
private CookieManagerImpl mCookieManager;
+ private PrerenderControllerImpl mPrerenderController;
private Runnable mOnDestroyCallback;
private boolean mBeingDeleted;
private boolean mDownloadsInitialized;
@@ -61,6 +63,8 @@ public static void enumerateAllProfileNames(ValueCallback callback) {
mNativeProfile = ProfileImplJni.get().createProfile(name, ProfileImpl.this);
mCookieManager =
new CookieManagerImpl(ProfileImplJni.get().getCookieManager(mNativeProfile));
+ mPrerenderController = new PrerenderControllerImpl(
+ ProfileImplJni.get().getPrerenderController(mNativeProfile));
mOnDestroyCallback = onDestroyCallback;
mDownloadCallbackProxy = new DownloadCallbackProxy(mName, mNativeProfile);
}
@@ -75,6 +79,11 @@ private void destroyDependentJavaObjects() {
mCookieManager.destroy();
mCookieManager = null;
}
+
+ if (mPrerenderController != null) {
+ mPrerenderController.destroy();
+ mPrerenderController = null;
+ }
}
@Override
@@ -185,6 +194,13 @@ public ICookieManager getCookieManager() {
return mCookieManager;
}
+ @Override
+ public IPrerenderController getPrerenderController() {
+ StrictModeWorkaround.apply();
+ checkNotDestroyed();
+ return mPrerenderController;
+ }
+
@Override
public void getBrowserPersistenceIds(@NonNull IObjectWrapper callback) {
StrictModeWorkaround.apply();
@@ -293,6 +309,7 @@ void clearBrowsingData(long nativeProfileImpl, @ImplBrowsingDataType int[] dataT
long fromMillis, long toMillis, Runnable callback);
void setDownloadDirectory(long nativeProfileImpl, String directory);
long getCookieManager(long nativeProfileImpl);
+ long getPrerenderController(long nativeProfileImpl);
void ensureBrowserContextInitialized(long nativeProfileImpl);
void setBooleanSetting(long nativeProfileImpl, int type, boolean value);
boolean getBooleanSetting(long nativeProfileImpl, int type);
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IPrerenderController.aidl b/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IPrerenderController.aidl
new file mode 100644
index 00000000000000..f5da9fe247ca4d
--- /dev/null
+++ b/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IPrerenderController.aidl
@@ -0,0 +1,10 @@
+// Copyright 2020 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.weblayer_private.interfaces;
+
+interface IPrerenderController {
+ // Since 88
+ void prerender(in String url) = 0;
+}
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IProfile.aidl b/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IProfile.aidl
index d647269aee785f..11fcde8708c8c9 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IProfile.aidl
+++ b/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IProfile.aidl
@@ -8,6 +8,7 @@ import org.chromium.weblayer_private.interfaces.ICookieManager;
import org.chromium.weblayer_private.interfaces.IDownloadCallbackClient;
import org.chromium.weblayer_private.interfaces.IUserIdentityCallbackClient;
import org.chromium.weblayer_private.interfaces.IObjectWrapper;
+import org.chromium.weblayer_private.interfaces.IPrerenderController;
interface IProfile {
void destroy() = 0;
@@ -44,4 +45,7 @@ interface IProfile {
// Added in Version 87.
void setUserIdentityCallbackClient(IUserIdentityCallbackClient client) = 13;
+
+ // Added in Version 88.
+ IPrerenderController getPrerenderController() = 15;
}
diff --git a/weblayer/browser/no_state_prefetch/no_state_prefetch_browsertest.cc b/weblayer/browser/no_state_prefetch/no_state_prefetch_browsertest.cc
index 0916b1e07ba13d..6c20eb885e5734 100644
--- a/weblayer/browser/no_state_prefetch/no_state_prefetch_browsertest.cc
+++ b/weblayer/browser/no_state_prefetch/no_state_prefetch_browsertest.cc
@@ -20,6 +20,7 @@
#include "weblayer/browser/no_state_prefetch/prerender_manager_factory.h"
#include "weblayer/browser/profile_impl.h"
#include "weblayer/browser/tab_impl.h"
+#include "weblayer/public/prerender_controller.h"
#include "weblayer/shell/browser/shell.h"
#include "weblayer/test/weblayer_browser_test.h"
#include "weblayer/test/weblayer_browser_test_utils.h"
@@ -68,15 +69,14 @@ class NoStatePrefetchBrowserTest : public WebLayerBrowserTest {
std::unique_ptr HandleRequest(
const net::test_server::HttpRequest& request) {
if (request.GetURL().path().find("prerendered_page") != std::string::npos) {
- if (prerendered_page_fetched_)
- prerendered_page_fetched_->Quit();
+ prerendered_page_fetched_->Quit();
+ prerendered_page_was_fetched_ = true;
}
if (request.GetURL().path().find("prefetch.js") != std::string::npos) {
script_fetched_ = true;
auto iter = request.headers.find("Purpose");
purpose_header_value_ = iter->second;
- if (script_resource_fetched_)
- script_resource_fetched_->Quit();
+ script_resource_fetched_->Quit();
}
if (request.GetURL().path().find("prefetch_meta.js") != std::string::npos) {
script_executed_ = true;
@@ -103,6 +103,7 @@ class NoStatePrefetchBrowserTest : public WebLayerBrowserTest {
std::unique_ptr prerendered_page_fetched_;
std::unique_ptr script_resource_fetched_;
+ bool prerendered_page_was_fetched_ = false;
bool script_fetched_ = false;
bool script_executed_ = false;
std::string purpose_header_value_;
@@ -205,4 +206,33 @@ IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, LinkRelNextWithNSPDisabled) {
prerendered_page_fetched_->Run();
}
-} // namespace weblayer
\ No newline at end of file
+// Non-web initiated prerender succeeds and subsequent navigations reuse
+// previously downloaded resources.
+IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, ExternalPrerender) {
+ // std::unique_ptr controller =
+ // PrerenderControllerImpl::Create(shell()->browser());
+ GetProfile()->GetPrerenderController()->Prerender(
+ GURL(https_server_->GetURL("/prerendered_page.html")));
+
+ script_resource_fetched_->Run();
+
+ // Navigate to the prerendered page and wait for its title to change.
+ script_fetched_ = false;
+ NavigateToPageAndWaitForTitleChange(
+ GURL(https_server_->GetURL("/prerendered_page.html")),
+ base::ASCIIToUTF16("Prefetch Page"));
+ EXPECT_FALSE(script_fetched_);
+}
+
+// Non-web initiated prerender fails when the user has opted out.
+IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest,
+ ExternalPrerenderWhenOptedOut) {
+ GetProfile()->SetBooleanSetting(SettingType::NETWORK_PREDICTION_ENABLED,
+ false);
+ GetProfile()->GetPrerenderController()->Prerender(
+ GURL(https_server_->GetURL("/prerendered_page.html")));
+ base::RunLoop().RunUntilIdle();
+ EXPECT_FALSE(prerendered_page_was_fetched_);
+}
+
+} // namespace weblayer
diff --git a/weblayer/browser/no_state_prefetch/prerender_controller_impl.cc b/weblayer/browser/no_state_prefetch/prerender_controller_impl.cc
new file mode 100644
index 00000000000000..a5dd8f788a11db
--- /dev/null
+++ b/weblayer/browser/no_state_prefetch/prerender_controller_impl.cc
@@ -0,0 +1,54 @@
+// Copyright 2020 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.
+
+#include "weblayer/browser/no_state_prefetch/prerender_controller_impl.h"
+
+#include "build/build_config.h"
+#include "components/prerender/browser/prerender_handle.h"
+#include "components/prerender/browser/prerender_manager.h"
+#include "content/public/browser/browser_context.h"
+#include "ui/gfx/geometry/rect.h"
+#include "url/gurl.h"
+#include "weblayer/browser/no_state_prefetch/prerender_manager_factory.h"
+
+#if defined(OS_ANDROID)
+#include "base/android/jni_string.h"
+#include "weblayer/browser/java/jni/PrerenderControllerImpl_jni.h"
+#endif
+
+namespace weblayer {
+
+PrerenderControllerImpl::PrerenderControllerImpl(
+ content::BrowserContext* browser_context)
+ : browser_context_(browser_context) {
+ DCHECK(browser_context_);
+}
+
+PrerenderControllerImpl::~PrerenderControllerImpl() = default;
+
+#if defined(OS_ANDROID)
+void PrerenderControllerImpl::Prerender(
+ JNIEnv* env,
+ const base::android::JavaParamRef& url) {
+ Prerender(GURL(ConvertJavaStringToUTF8(url)));
+}
+#endif
+
+void PrerenderControllerImpl::Prerender(const GURL& url) {
+ auto* prerender_manager =
+ PrerenderManagerFactory::GetForBrowserContext(browser_context_);
+ DCHECK(prerender_manager);
+
+ // The referrer parameter results in a header being set that lets the server
+ // serving the URL being prerendered see where the request originated. It's
+ // an optional header, it's okay to skip setting it here.
+ // SessionStorageNamespace isn't necessary for NoStatePrefetch, so it's okay
+ // to pass in a nullptr.
+ // PrerenderManager uses default bounds if the one provided is empty.
+ prerender_manager->AddPrerenderFromExternalRequest(
+ url, content::Referrer(), /* session_storage_namespace= */ nullptr,
+ /* bounds= */ gfx::Rect());
+}
+
+} // namespace weblayer
diff --git a/weblayer/browser/no_state_prefetch/prerender_controller_impl.h b/weblayer/browser/no_state_prefetch/prerender_controller_impl.h
new file mode 100644
index 00000000000000..eb368a9be2bce6
--- /dev/null
+++ b/weblayer/browser/no_state_prefetch/prerender_controller_impl.h
@@ -0,0 +1,46 @@
+// Copyright 2020 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.
+
+#ifndef WEBLAYER_BROWSER_NO_STATE_PREFETCH_PRERENDER_CONTROLLER_IMPL_H_
+#define WEBLAYER_BROWSER_NO_STATE_PREFETCH_PRERENDER_CONTROLLER_IMPL_H_
+
+#include
+
+#include "build/build_config.h"
+#include "weblayer/public/prerender_controller.h"
+
+#if defined(OS_ANDROID)
+#include "base/android/scoped_java_ref.h"
+#endif
+
+class GURL;
+
+namespace content {
+class BrowserContext;
+}
+
+namespace weblayer {
+
+// Enables creation of a non-web initiated prerender request.
+class PrerenderControllerImpl : public PrerenderController {
+ public:
+ explicit PrerenderControllerImpl(content::BrowserContext* browser_context);
+ ~PrerenderControllerImpl() override;
+ PrerenderControllerImpl(const PrerenderControllerImpl&) = delete;
+ PrerenderControllerImpl& operator=(const PrerenderControllerImpl&) = delete;
+
+#if defined(OS_ANDROID)
+ void Prerender(JNIEnv* env, const base::android::JavaParamRef& url);
+#endif
+
+ // PrerenderController
+ void Prerender(const GURL& url) override;
+
+ private:
+ content::BrowserContext* browser_context_;
+};
+
+} // namespace weblayer
+
+#endif // WEBLAYER_BROWSER_NO_STATE_PREFETCH_PRERENDER_CONTROLLER_IMPL_H_
diff --git a/weblayer/browser/profile_impl.cc b/weblayer/browser/profile_impl.cc
index d353f19cb3f8ba..6f4f93181dcb76 100644
--- a/weblayer/browser/profile_impl.cc
+++ b/weblayer/browser/profile_impl.cc
@@ -40,6 +40,7 @@
#include "weblayer/browser/cookie_manager_impl.h"
#include "weblayer/browser/favicon/favicon_service_impl.h"
#include "weblayer/browser/favicon/favicon_service_impl_factory.h"
+#include "weblayer/browser/no_state_prefetch/prerender_controller_impl.h"
#include "weblayer/browser/persistence/browser_persister_file_utils.h"
#include "weblayer/browser/tab_impl.h"
@@ -314,6 +315,13 @@ CookieManager* ProfileImpl::GetCookieManager() {
return cookie_manager_.get();
}
+PrerenderController* ProfileImpl::GetPrerenderController() {
+ if (!prerender_controller_)
+ prerender_controller_ =
+ std::make_unique(GetBrowserContext());
+ return prerender_controller_.get();
+}
+
void ProfileImpl::GetBrowserPersistenceIds(
base::OnceCallback)> callback) {
DCHECK(!browser_context_->IsOffTheRecord());
@@ -511,6 +519,10 @@ jlong ProfileImpl::GetCookieManager(JNIEnv* env) {
return reinterpret_cast(GetCookieManager());
}
+jlong ProfileImpl::GetPrerenderController(JNIEnv* env) {
+ return reinterpret_cast(GetPrerenderController());
+}
+
void ProfileImpl::EnsureBrowserContextInitialized(JNIEnv* env) {
content::BrowserContext::GetDownloadManager(GetBrowserContext());
}
diff --git a/weblayer/browser/profile_impl.h b/weblayer/browser/profile_impl.h
index 5707d850ac993f..07c5cd923b8239 100644
--- a/weblayer/browser/profile_impl.h
+++ b/weblayer/browser/profile_impl.h
@@ -30,6 +30,7 @@ class WebContents;
namespace weblayer {
class BrowserContextImpl;
class CookieManagerImpl;
+class PrerenderControllerImpl;
class ProfileImpl : public Profile {
public:
@@ -91,6 +92,7 @@ class ProfileImpl : public Profile {
void SetDownloadDirectory(const base::FilePath& directory) override;
void SetDownloadDelegate(DownloadDelegate* delegate) override;
CookieManager* GetCookieManager() override;
+ PrerenderController* GetPrerenderController() override;
void GetBrowserPersistenceIds(
base::OnceCallback)> callback) override;
void RemoveBrowserPersistenceStorage(
@@ -123,6 +125,7 @@ class ProfileImpl : public Profile {
JNIEnv* env,
const base::android::JavaParamRef& directory);
jlong GetCookieManager(JNIEnv* env);
+ jlong GetPrerenderController(JNIEnv* env);
void EnsureBrowserContextInitialized(JNIEnv* env);
void SetBooleanSetting(JNIEnv* env, jint j_type, jboolean j_value);
jboolean GetBooleanSetting(JNIEnv* env, jint j_type);
@@ -176,6 +179,7 @@ class ProfileImpl : public Profile {
std::unique_ptr locale_change_subscription_;
std::unique_ptr cookie_manager_;
+ std::unique_ptr prerender_controller_;
#if defined(OS_ANDROID)
base::android::ScopedJavaGlobalRef java_profile_;
diff --git a/weblayer/public/java/BUILD.gn b/weblayer/public/java/BUILD.gn
index a76b3cf1421ac3..3252520f7e631c 100644
--- a/weblayer/public/java/BUILD.gn
+++ b/weblayer/public/java/BUILD.gn
@@ -81,6 +81,7 @@ android_library("java") {
"org/chromium/weblayer/NewTabCallback.java",
"org/chromium/weblayer/NewTabType.java",
"org/chromium/weblayer/ObserverList.java",
+ "org/chromium/weblayer/PrerenderController.java",
"org/chromium/weblayer/Profile.java",
"org/chromium/weblayer/RemoteFragment.java",
"org/chromium/weblayer/RemoteMediaService.java",
diff --git a/weblayer/public/java/org/chromium/weblayer/PrerenderController.java b/weblayer/public/java/org/chromium/weblayer/PrerenderController.java
new file mode 100644
index 00000000000000..78f9a6f9a0e237
--- /dev/null
+++ b/weblayer/public/java/org/chromium/weblayer/PrerenderController.java
@@ -0,0 +1,66 @@
+// Copyright 2020 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.weblayer;
+
+import android.net.Uri;
+import android.os.RemoteException;
+
+import androidx.annotation.NonNull;
+
+import org.chromium.weblayer_private.interfaces.APICallException;
+import org.chromium.weblayer_private.interfaces.IPrerenderController;
+import org.chromium.weblayer_private.interfaces.IProfile;
+
+/**
+ * PrerenderController enables prerendering of urls.
+ *
+ * Prerendering has the same effect as adding a link rel="prerender" resource hint to a web page. It
+ * is implemented using NoStatePrefetch and fetches resources needed for a url in advance, but does
+ * not execute Javascript or render any part of the page in advance. For more information on
+ * NoStatePrefetch, see https://developers.google.com/web/updates/2018/07/nostate-prefetch.
+ *
+ * @since 88
+ */
+public class PrerenderController {
+ private final IPrerenderController mImpl;
+
+ static PrerenderController create(IProfile profile) {
+ try {
+ return new PrerenderController(profile.getPrerenderController());
+ } catch (RemoteException e) {
+ throw new APICallException(e);
+ }
+ }
+
+ // Constructor for test mocking.
+ protected PrerenderController() {
+ mImpl = null;
+ }
+
+ PrerenderController(IPrerenderController prerenderController) {
+ mImpl = prerenderController;
+ }
+
+ /*
+ * Creates a prerender for the url provided.
+ * Prerendering here is implemented using NoStatePrefetch. We fetch resources and save them
+ * to the HTTP cache. All resources are cached according to their cache headers. For details,
+ * see https://developers.google.com/web/updates/2018/07/nostate-prefetch#implementation.
+ *
+ * On low end devices or when the device has too many renderers running and prerender is
+ * considered expensive, we do preconnect instead. Preconnect involves creating connections with
+ * the server without actually fetching any resources. For more information on preconnect, see
+ * https://www.chromium.org/developers/design-documents/network-stack/preconnect.
+ * @param uri The uri to prerender.
+ */
+ public void schedulePrerender(@NonNull Uri uri) {
+ ThreadCheck.ensureOnUiThread();
+ try {
+ mImpl.prerender(uri.toString());
+ } catch (RemoteException exception) {
+ throw new APICallException(exception);
+ }
+ }
+}
diff --git a/weblayer/public/java/org/chromium/weblayer/Profile.java b/weblayer/public/java/org/chromium/weblayer/Profile.java
index 1b05f31f5a085b..f29758e995e743 100644
--- a/weblayer/public/java/org/chromium/weblayer/Profile.java
+++ b/weblayer/public/java/org/chromium/weblayer/Profile.java
@@ -66,18 +66,25 @@ public static Collection getAllProfiles() {
private IProfile mImpl;
private DownloadCallbackClientImpl mDownloadCallbackClient;
private final CookieManager mCookieManager;
+ private final PrerenderController mPrerenderController;
// Constructor for test mocking.
protected Profile() {
mName = null;
mImpl = null;
mCookieManager = null;
+ mPrerenderController = null;
}
private Profile(String name, IProfile impl) {
mName = name;
mImpl = impl;
mCookieManager = CookieManager.create(impl);
+ if (WebLayer.getSupportedMajorVersionInternal() >= 88) {
+ mPrerenderController = PrerenderController.create(impl);
+ } else {
+ mPrerenderController = null;
+ }
sProfiles.put(name, this);
}
@@ -198,6 +205,21 @@ public CookieManager getCookieManager() {
return mCookieManager;
}
+ /**
+ * Gets the prerender controller for this profile.
+ *
+ * @since 88
+ */
+ @NonNull
+ public PrerenderController getPrerenderController() {
+ if (WebLayer.getSupportedMajorVersionInternal() < 88) {
+ throw new UnsupportedOperationException();
+ }
+
+ ThreadCheck.ensureOnUiThread();
+ return mPrerenderController;
+ }
+
/**
* Allows the embedder to set a boolean value for a specific setting, see {@link SettingType}
* for more details and the possible options.
diff --git a/weblayer/public/prerender_controller.h b/weblayer/public/prerender_controller.h
new file mode 100644
index 00000000000000..96e5365bb35f42
--- /dev/null
+++ b/weblayer/public/prerender_controller.h
@@ -0,0 +1,31 @@
+// Copyright 2020 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.
+
+#ifndef WEBLAYER_PUBLIC_PRERENDER_CONTROLLER_H_
+#define WEBLAYER_PUBLIC_PRERENDER_CONTROLLER_H_
+
+#include
+
+class GURL;
+
+namespace weblayer {
+
+// PrerenderController enables prerendering of urls.
+// Prerendering has the same effect as adding a link rel="prerender" resource
+// hint to a web page. It is implemented using NoStatePrefetch and fetches
+// resources needed for a url in advance, but does not execute Javascript or
+// render any part of the page in advance. For more information on
+// NoStatePrefetch, see
+// https://developers.google.com/web/updates/2018/07/nostate-prefetch.
+class PrerenderController {
+ public:
+ virtual void Prerender(const GURL& url) = 0;
+
+ protected:
+ virtual ~PrerenderController() = default;
+};
+
+} // namespace weblayer
+
+#endif // WEBLAYER_PUBLIC_PRERENDER_CONTROLLER_H_
diff --git a/weblayer/public/profile.h b/weblayer/public/profile.h
index e5d8b5417a576d..59d26c42c3a071 100644
--- a/weblayer/public/profile.h
+++ b/weblayer/public/profile.h
@@ -25,6 +25,7 @@ class GURL;
namespace weblayer {
class CookieManager;
class DownloadDelegate;
+class PrerenderController;
// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.weblayer_private
// GENERATED_JAVA_CLASS_NAME_OVERRIDE: ImplBrowsingDataType
@@ -77,6 +78,9 @@ class Profile {
// Gets the cookie manager for this profile.
virtual CookieManager* GetCookieManager() = 0;
+ // Gets the prerender controller for this profile.
+ virtual PrerenderController* GetPrerenderController() = 0;
+
// Asynchronously fetches the set of known Browser persistence-ids. See
// Browser::PersistenceInfo for more details on persistence-ids.
virtual void GetBrowserPersistenceIds(