Commit 177133f6 authored by yusufo@chromium.org's avatar yusufo@chromium.org

Add ExternalPrerenderRequestHandler and related API

This class will be used to handle prerender requests coming from external applications.
For better tracking and also supportig referrer, a separate PrerenderManager API is
added.

BUG=297859

Review URL: https://codereview.chromium.org/45693002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@233457 0039d316-1c4b-4281-b951-d872f2087c98
parent 33590f0e
// Copyright 2013 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.prerender;
import org.chromium.base.JNINamespace;
import org.chromium.chrome.browser.ContentViewUtil;
import org.chromium.chrome.browser.profiles.Profile;
/**
* A handler class for prerender requests coming from other applications.
*/
@JNINamespace("prerender")
public class ExternalPrerenderHandler {
private int mNativeExternalPrerenderHandler;
public ExternalPrerenderHandler() {
mNativeExternalPrerenderHandler = nativeInit();
}
public int addPrerender(Profile profile, String url, String referrer, int width, int height) {
int webContentsPtr = ContentViewUtil.createNativeWebContents(false);
if (nativeAddPrerender(mNativeExternalPrerenderHandler, profile, webContentsPtr,
url, referrer, width, height)) {
return webContentsPtr;
}
ContentViewUtil.destroyNativeWebContents(webContentsPtr);
return 0;
}
public void cancelCurrentPrerender() {
nativeCancelCurrentPrerender(mNativeExternalPrerenderHandler);
}
public static boolean hasPrerenderedUrl(Profile profile, String url, int webContentsPtr) {
return nativeHasPrerenderedUrl(profile, url, webContentsPtr);
}
private static native int nativeInit();
private static native boolean nativeAddPrerender(
int nativeExternalPrerenderHandlerAndroid, Profile profile,
int webContentsPtr, String url, String referrer, int width, int height);
private static native boolean nativeHasPrerenderedUrl(
Profile profile, String url, int webContentsPtr);
private static native void nativeCancelCurrentPrerender(
int nativeExternalPrerenderHandlerAndroid);
}
// Copyright 2013 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.prerender;
import android.test.UiThreadTest;
import android.test.suitebuilder.annotation.MediumTest;
import android.test.suitebuilder.annotation.SmallTest;
import org.chromium.base.ThreadUtils;
import org.chromium.base.test.util.Feature;
import org.chromium.chrome.browser.ContentViewUtil;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.test.util.TestHttpServerClient;
import org.chromium.chrome.testshell.ChromiumTestShellTestBase;
import java.util.concurrent.Callable;
/**
* Tests for adding and removing prerenders using the {@link ExternalPrerenderHandler}
*/
public class ExternalPrerenderRequestTest extends ChromiumTestShellTestBase {
private static final String GOOGLE_URL =
TestHttpServerClient.getUrl("chrome/test/data/android/prerender/google.html");
private static final String YOUTUBE_URL =
TestHttpServerClient.getUrl("chrome/test/data/android/prerender/youtube.html");
private static final int PRERENDER_DELAY_MS = 500;
private ExternalPrerenderHandler mHandler;
private Profile mProfile;
@Override
public void setUp() throws Exception {
super.setUp();
clearAppData();
launchChromiumTestShellWithBlankPage();
assertTrue(waitForActiveShellToBeDoneLoading());
mHandler = new ExternalPrerenderHandler();
final Callable<Profile> profileCallable = new Callable<Profile>() {
@Override
public Profile call() throws Exception {
return Profile.getLastUsedProfile();
}
};
mProfile = ThreadUtils.runOnUiThreadBlocking(profileCallable);
}
@MediumTest
@UiThreadTest
@Feature({"Prerender"})
/**
* Test adding a prerender and canceling that to add a new one.
*/
public void testAddPrerenderAndCancel() throws InterruptedException {
int webContentsPtr = mHandler.addPrerender(mProfile, GOOGLE_URL, "", 0, 0);
assertTrue(ExternalPrerenderHandler.hasPrerenderedUrl(
mProfile, GOOGLE_URL, webContentsPtr));
mHandler.cancelCurrentPrerender();
assertFalse(ExternalPrerenderHandler.hasPrerenderedUrl(
mProfile, GOOGLE_URL, webContentsPtr));
ContentViewUtil.destroyNativeWebContents(webContentsPtr);
Thread.sleep(PRERENDER_DELAY_MS);
webContentsPtr = mHandler.addPrerender(mProfile, YOUTUBE_URL, "", 0, 0);
assertTrue(ExternalPrerenderHandler.hasPrerenderedUrl(
mProfile, YOUTUBE_URL, webContentsPtr));
}
@SmallTest
@UiThreadTest
@Feature({"Prerender"})
/**
* Test calling cancel without any added prerenders.
*/
public void testCancelPrerender() {
mHandler.cancelCurrentPrerender();
int webContentsPtr = mHandler.addPrerender(mProfile, GOOGLE_URL, "", 0, 0);
assertTrue(ExternalPrerenderHandler.hasPrerenderedUrl(
mProfile, GOOGLE_URL, webContentsPtr));
}
@MediumTest
@UiThreadTest
@Feature({"Prerender"})
/**
* Test adding two prerenders without canceling the first one.
*/
public void testAddingPrerendersInaRow() throws InterruptedException {
int webContentsPtr = mHandler.addPrerender(mProfile, GOOGLE_URL, "", 0, 0);
assertTrue(ExternalPrerenderHandler.hasPrerenderedUrl(
mProfile, GOOGLE_URL, webContentsPtr));
Thread.sleep(PRERENDER_DELAY_MS);
int newWebContentsPtr = mHandler.addPrerender(mProfile, YOUTUBE_URL, "", 0, 0);
assertTrue(ExternalPrerenderHandler.hasPrerenderedUrl(
mProfile, YOUTUBE_URL, newWebContentsPtr));
}
}
......@@ -31,6 +31,7 @@
#include "chrome/browser/invalidation/invalidation_controller_android.h"
#include "chrome/browser/lifetime/application_lifetime_android.h"
#include "chrome/browser/net/spdyproxy/data_reduction_proxy_settings_android.h"
#include "chrome/browser/prerender/external_prerender_handler_android.h"
#include "chrome/browser/profiles/profile_android.h"
#include "chrome/browser/search_engines/template_url_service_android.h"
#include "chrome/browser/signin/android_profile_oauth2_token_service.h"
......@@ -93,6 +94,9 @@ static base::android::RegistrationMethod kChromeRegisteredMethods[] = {
{ "DataReductionProxySettings", DataReductionProxySettingsAndroid::Register },
{ "DevToolsServer", RegisterDevToolsServer },
{ "InvalidationController", invalidation::RegisterInvalidationController },
{ "ExternalPrerenderRequestHandler",
prerender::ExternalPrerenderHandlerAndroid::
RegisterExternalPrerenderHandlerAndroid },
{ "FaviconHelper", FaviconHelper::RegisterFaviconHelper },
{ "FieldTrialHelper", RegisterFieldTrialHelper },
{ "ForeignSessionHelper",
......
// Copyright 2013 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 "chrome/browser/prerender/external_prerender_handler_android.h"
#include "base/android/jni_android.h"
#include "base/android/jni_string.h"
#include "base/strings/string16.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/prerender/prerender_handle.h"
#include "chrome/browser/prerender/prerender_manager.h"
#include "chrome/browser/prerender/prerender_manager_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_android.h"
#include "content/public/browser/web_contents.h"
#include "jni/ExternalPrerenderHandler_jni.h"
using base::android::ConvertJavaStringToUTF16;
namespace prerender {
bool ExternalPrerenderHandlerAndroid::AddPrerender(JNIEnv* env,
jobject obj,
jobject jprofile,
jint web_contents_ptr,
jstring jurl,
jstring jreferrer,
jint width,
jint height) {
Profile* profile = ProfileAndroid::FromProfileAndroid(jprofile);
GURL url = GURL(ConvertJavaStringToUTF16(env, jurl));
if (!url.is_valid())
return false;
GURL referrer_url = GURL(ConvertJavaStringToUTF16(env, jreferrer));
content::Referrer referrer = referrer_url.is_valid() ?
content::Referrer(referrer_url, WebKit::WebReferrerPolicyDefault) :
content::Referrer();
PrerenderManager* prerender_manager =
prerender::PrerenderManagerFactory::GetForProfile(profile);
if (!prerender_manager)
return false;
content::WebContents* web_contents =
reinterpret_cast<content::WebContents*>(web_contents_ptr);
if (prerender_handle_.get()) {
prerender_handle_->OnNavigateAway();
}
prerender_handle_.reset(
prerender_manager->AddPrerenderFromExternalRequest(
url,
referrer,
web_contents->GetController().GetDefaultSessionStorageNamespace(),
gfx::Size(width, height)));
if (!prerender_handle_)
return false;
return true;
}
void ExternalPrerenderHandlerAndroid::CancelCurrentPrerender(JNIEnv* env,
jobject object) {
if (!prerender_handle_)
return;
prerender_handle_->OnCancel();
prerender_handle_.reset();
}
static jboolean HasPrerenderedUrl(JNIEnv* env,
jclass clazz,
jobject jprofile,
jstring jurl,
jint web_contents_ptr) {
if (jurl == NULL)
return false;
Profile* profile = ProfileAndroid::FromProfileAndroid(jprofile);
GURL url = GURL(ConvertJavaStringToUTF16(env, jurl));
if (!url.is_valid())
return false;
prerender::PrerenderManager* prerender_manager =
prerender::PrerenderManagerFactory::GetForProfile(profile);
if (!prerender_manager)
return false;
content::WebContents* web_contents =
reinterpret_cast<content::WebContents*>(web_contents_ptr);
return prerender_manager->HasPrerenderedUrl(url, web_contents);
}
ExternalPrerenderHandlerAndroid::ExternalPrerenderHandlerAndroid() {}
ExternalPrerenderHandlerAndroid::~ExternalPrerenderHandlerAndroid() {}
static jint Init(JNIEnv* env, jclass clazz) {
ExternalPrerenderHandlerAndroid* external_handler =
new ExternalPrerenderHandlerAndroid();
return reinterpret_cast<jint>(external_handler);
}
bool ExternalPrerenderHandlerAndroid::RegisterExternalPrerenderHandlerAndroid(
JNIEnv* env) {
return RegisterNativesImpl(env);
}
} // namespace prerender
// Copyright 2013 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 CHROME_BROWSER_PRERENDER_EXTERNAL_PRERENDER_HANDLER_ANDROID_H_
#define CHROME_BROWSER_PRERENDER_EXTERNAL_PRERENDER_HANDLER_ANDROID_H_
#include <jni.h>
#include "base/android/scoped_java_ref.h"
#include "base/memory/scoped_ptr.h"
class GURL;
class Profile;
namespace content {
class WebContents;
}
namespace prerender {
class PrerenderHandle;
// A class for handling external prerender requests from other applications.
class ExternalPrerenderHandlerAndroid {
public:
explicit ExternalPrerenderHandlerAndroid();
// Add a prerender with the given url and referrer on the PrerenderManager of
// the given profile. This is restricted to a single prerender at a time.
bool AddPrerender(JNIEnv* env,
jobject obj,
jobject profile,
jint web_content_ptr,
jstring url,
jstring referrer,
jint width,
jint height);
// Cancel the prerender associated with the prerender_handle_
void CancelCurrentPrerender(JNIEnv* env, jobject object);
// Whether the PrerenderManager associated with the given profile has any
// prerenders for the url.
static bool HasPrerenderedUrl(Profile* profile,
GURL url,
content::WebContents* web_contents);
static bool RegisterExternalPrerenderHandlerAndroid(JNIEnv* env);
private:
virtual ~ExternalPrerenderHandlerAndroid();
scoped_ptr<prerender::PrerenderHandle> prerender_handle_;
DISALLOW_COPY_AND_ASSIGN(ExternalPrerenderHandlerAndroid);
};
} // namespace prerender
#endif // CHROME_BROWSER_PRERENDER_EXTERNAL_PRERENDER_HANDLER_ANDROID_H_
......@@ -58,6 +58,8 @@ std::string GetHistogramName(Origin origin, uint8 experiment_id,
return ComposeHistogramName("webcross", name);
case ORIGIN_LOCAL_PREDICTOR:
return ComposeHistogramName("localpredictor", name);
case ORIGIN_EXTERNAL_REQUEST:
return ComposeHistogramName("externalrequest", name);
case ORIGIN_GWS_PRERENDER: // Handled above.
default:
NOTREACHED();
......@@ -117,6 +119,8 @@ bool OriginIsOmnibox(Origin origin) {
HISTOGRAM; \
} else if (origin == ORIGIN_LOCAL_PREDICTOR) { \
HISTOGRAM; \
} else if (origin == ORIGIN_EXTERNAL_REQUEST) { \
HISTOGRAM; \
} else if (experiment != kNoExperiment) { \
HISTOGRAM; \
} else { \
......
......@@ -400,6 +400,15 @@ PrerenderHandle* PrerenderManager::AddPrerenderFromLocalPredictor(
size, session_storage_namespace);
}
PrerenderHandle* PrerenderManager::AddPrerenderFromExternalRequest(
const GURL& url,
const content::Referrer& referrer,
SessionStorageNamespace* session_storage_namespace,
const gfx::Size& size) {
return AddPrerender(ORIGIN_EXTERNAL_REQUEST, -1, url, referrer, size,
session_storage_namespace);
}
void PrerenderManager::DestroyPrerenderForRenderView(
int process_id, int view_id, FinalStatus final_status) {
DCHECK(CalledOnValidThread());
......@@ -794,6 +803,23 @@ bool PrerenderManager::IsWebContentsPrerendering(
return false;
}
bool PrerenderManager::HasPrerenderedUrl(
GURL url,
content::WebContents* web_contents) const {
content::SessionStorageNamespace* session_storage_namespace = web_contents->
GetController().GetDefaultSessionStorageNamespace();
for (ScopedVector<PrerenderData>::const_iterator it =
active_prerenders_.begin();
it != active_prerenders_.end(); ++it) {
PrerenderContents* prerender_contents = (*it)->contents();
if (prerender_contents->Matches(url, session_storage_namespace)) {
return true;
}
}
return false;
}
PrerenderContents* PrerenderManager::GetPrerenderContents(
const content::WebContents* web_contents) const {
DCHECK(CalledOnValidThread());
......
......@@ -151,6 +151,12 @@ class PrerenderManager : public base::SupportsWeakPtr<PrerenderManager>,
content::SessionStorageNamespace* session_storage_namespace,
const gfx::Size& size);
PrerenderHandle* AddPrerenderFromExternalRequest(
const GURL& url,
const content::Referrer& referrer,
content::SessionStorageNamespace* session_storage_namespace,
const gfx::Size& size);
// If |process_id| and |view_id| refer to a running prerender, destroy
// it with |final_status|.
virtual void DestroyPrerenderForRenderView(int process_id,
......@@ -215,6 +221,10 @@ class PrerenderManager : public base::SupportsWeakPtr<PrerenderManager>,
bool IsWebContentsPrerendering(const content::WebContents* web_contents,
Origin* origin) const;
// Whether the PrerenderManager has an active prerender with the given url and
// SessionStorageNamespace associated with the given WebContens.
bool HasPrerenderedUrl(GURL url, content::WebContents* web_contents) const;
// Returns the PrerenderContents object for the given web_contents if it's
// used for an active prerender page, otherwise returns NULL.
PrerenderContents* GetPrerenderContents(
......
......@@ -22,6 +22,7 @@ const char* kOriginNames[] = {
"Link Rel Prerender (same domain)",
"Link Rel Prerender (cross domain)",
"Local Predictor",
"External Request",
"Max",
};
COMPILE_ASSERT(arraysize(kOriginNames) == ORIGIN_MAX + 1,
......
......@@ -20,6 +20,7 @@ enum Origin {
ORIGIN_LINK_REL_PRERENDER_SAMEDOMAIN = 7,
ORIGIN_LINK_REL_PRERENDER_CROSSDOMAIN = 8,
ORIGIN_LOCAL_PREDICTOR = 9,
ORIGIN_EXTERNAL_REQUEST = 10,
ORIGIN_MAX,
};
......
......@@ -1633,6 +1633,8 @@
'browser/prefs/synced_pref_change_registrar.cc',
'browser/prefs/synced_pref_change_registrar.h',
'browser/prefs/synced_pref_observer.h',
'browser/prerender/external_prerender_handler_android.cc',
'browser/prerender/external_prerender_handler_android.h',
'browser/prerender/prerender_condition.h',
'browser/prerender/prerender_config.cc',
'browser/prerender/prerender_config.h',
......@@ -3682,6 +3684,7 @@
'android/java/src/org/chromium/chrome/browser/omnibox/OmniboxPrerender.java',
'android/java/src/org/chromium/chrome/browser/profiles/MostVisitedSites.java',
'android/java/src/org/chromium/chrome/browser/profiles/Profile.java',
'android/java/src/org/chromium/chrome/browser/prerender/ExternalPrerenderHandler.java',
'android/java/src/org/chromium/chrome/browser/RecentlyClosedBridge.java',
'android/java/src/org/chromium/chrome/browser/ShortcutHelper.java',
'android/java/src/org/chromium/chrome/browser/SSLClientCertificateRequest.java',
......
<html>
<head>
<title>Welcome to the Google</title>
</head>
<body>
<form>
<input type="text" id="textField"></input>
<input type="submit" value="Search"></input>
</form>
</body>
</html>
<html>
<head>
<title>Welcome to the YouTube</title>
</head>
<body>
Your place for the best lol catz.
</body>
</html>
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