bug 713503 - prefetch urls from known url shortening sites before gecko is running r=mfinkle
This commit is contained in:
@@ -48,5 +48,19 @@ public class App extends GeckoApp {
|
|||||||
public String getContentProcessName() {
|
public String getContentProcessName() {
|
||||||
return "@MOZ_CHILD_PROCESS_NAME@";
|
return "@MOZ_CHILD_PROCESS_NAME@";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getDefaultUAString() {
|
||||||
|
return "Mozilla/5.0 (Android; Linux armv7l; rv:@MOZ_APP_VERSION@) Gecko/@UA_BUILDID@ Firefox/@MOZ_APP_VERSION@ Fennec/@MOZ_APP_VERSION@";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUAStringForHost(String host) {
|
||||||
|
// With our standard UA String, we get a 200 response code and
|
||||||
|
// client-side redirect from t.co. This slight tweak gives us a
|
||||||
|
// 302 response code
|
||||||
|
if ("t.co".equals(host))
|
||||||
|
return "Mozilla/5.0 (Android; Linux armv7l; rv:@MOZ_APP_VERSION@) Gecko/@UA_BUILDID@ Firefox Mobile/@MOZ_APP_VERSION@";
|
||||||
|
return getDefaultUAString();
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -98,6 +98,7 @@ abstract public class GeckoApp
|
|||||||
public static final String ACTION_WEBAPP = "org.mozilla.gecko.WEBAPP";
|
public static final String ACTION_WEBAPP = "org.mozilla.gecko.WEBAPP";
|
||||||
public static final String ACTION_DEBUG = "org.mozilla.gecko.DEBUG";
|
public static final String ACTION_DEBUG = "org.mozilla.gecko.DEBUG";
|
||||||
public static final String ACTION_BOOKMARK = "org.mozilla.gecko.BOOKMARK";
|
public static final String ACTION_BOOKMARK = "org.mozilla.gecko.BOOKMARK";
|
||||||
|
public static final String ACTION_LOAD = "org.mozilla.gecko.LOAD";
|
||||||
public static final String SAVED_STATE_URI = "uri";
|
public static final String SAVED_STATE_URI = "uri";
|
||||||
public static final String SAVED_STATE_TITLE = "title";
|
public static final String SAVED_STATE_TITLE = "title";
|
||||||
public static final String SAVED_STATE_VIEWPORT = "viewport";
|
public static final String SAVED_STATE_VIEWPORT = "viewport";
|
||||||
@@ -1446,9 +1447,23 @@ abstract public class GeckoApp
|
|||||||
if (sGREDir == null)
|
if (sGREDir == null)
|
||||||
sGREDir = new File(this.getApplicationInfo().dataDir);
|
sGREDir = new File(this.getApplicationInfo().dataDir);
|
||||||
|
|
||||||
prefetchDNS(intent.getData());
|
String passedUri = mLastUri;
|
||||||
|
|
||||||
sGeckoThread = new GeckoThread(intent, mLastUri, mRestoreSession);
|
Uri data = intent.getData();
|
||||||
|
if (data != null && "http".equals(data.getScheme()) &&
|
||||||
|
isHostOnPrefetchWhitelist(data.getHost())) {
|
||||||
|
Intent copy = new Intent(intent);
|
||||||
|
copy.setAction(ACTION_LOAD);
|
||||||
|
GeckoAppShell.getHandler().post(new RedirectorRunnable(copy));
|
||||||
|
// We're going to handle this uri with the redirector, so setting
|
||||||
|
// the action to MAIN and clearing the uri data prevents us from
|
||||||
|
// loading it twice
|
||||||
|
intent.setAction(Intent.ACTION_MAIN);
|
||||||
|
intent.setData(null);
|
||||||
|
passedUri = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
sGeckoThread = new GeckoThread(intent, passedUri, mRestoreSession);
|
||||||
if (!ACTION_DEBUG.equals(intent.getAction()) &&
|
if (!ACTION_DEBUG.equals(intent.getAction()) &&
|
||||||
checkAndSetLaunchState(LaunchState.Launching, LaunchState.Launched))
|
checkAndSetLaunchState(LaunchState.Launching, LaunchState.Launched))
|
||||||
sGeckoThread.start();
|
sGeckoThread.start();
|
||||||
@@ -1646,6 +1661,76 @@ abstract public class GeckoApp
|
|||||||
mMainLayout.removeView(cameraView);
|
mMainLayout.removeView(cameraView);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abstract public String getDefaultUAString();
|
||||||
|
abstract public String getUAStringForHost(String host);
|
||||||
|
|
||||||
|
class RedirectorRunnable implements Runnable {
|
||||||
|
Intent mIntent;
|
||||||
|
RedirectorRunnable(Intent intent) {
|
||||||
|
mIntent = intent;
|
||||||
|
}
|
||||||
|
public void run() {
|
||||||
|
HttpURLConnection connection = null;
|
||||||
|
try {
|
||||||
|
// this class should only be initialized with an intent with non-null data
|
||||||
|
URL url = new URL(mIntent.getData().toString());
|
||||||
|
// data url should have an http scheme
|
||||||
|
connection = (HttpURLConnection) url.openConnection();
|
||||||
|
connection.setRequestProperty("User-Agent", getUAStringForHost(url.getHost()));
|
||||||
|
connection.setInstanceFollowRedirects(false);
|
||||||
|
connection.setRequestMethod("GET");
|
||||||
|
connection.connect();
|
||||||
|
int code = connection.getResponseCode();
|
||||||
|
if (code >= 300 && code < 400) {
|
||||||
|
String location = connection.getHeaderField("Location");
|
||||||
|
Uri data;
|
||||||
|
if (location != null &&
|
||||||
|
(data = Uri.parse(location)) != null &&
|
||||||
|
!"about".equals(data.getScheme()) &&
|
||||||
|
!"chrome".equals(data.getScheme())) {
|
||||||
|
mIntent.setData(data);
|
||||||
|
mLastUri = mLastTitle = location;
|
||||||
|
} else {
|
||||||
|
mIntent.putExtra("prefetched", 1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mIntent.putExtra("prefetched", 1);
|
||||||
|
}
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
Log.i(LOGTAG, "exception trying to pre-fetch redirected url", ioe);
|
||||||
|
mIntent.putExtra("prefetched", 1);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.w(LOGTAG, "unexpected exception, passing url directly to Gecko but we should explicitly catch this", e);
|
||||||
|
mIntent.putExtra("prefetched", 1);
|
||||||
|
} finally {
|
||||||
|
if (connection != null)
|
||||||
|
connection.disconnect();
|
||||||
|
}
|
||||||
|
mMainHandler.postAtFrontOfQueue(new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
onNewIntent(mIntent);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final String kPrefetchWhiteListArray[] = new String[] {
|
||||||
|
"t.co",
|
||||||
|
"bit.ly",
|
||||||
|
"moz.la",
|
||||||
|
"aje.me",
|
||||||
|
"facebook.com",
|
||||||
|
"goo.gl",
|
||||||
|
"tinyurl.com"
|
||||||
|
};
|
||||||
|
|
||||||
|
private final CopyOnWriteArrayList<String> kPrefetchWhiteList =
|
||||||
|
new CopyOnWriteArrayList<String>(kPrefetchWhiteListArray);
|
||||||
|
|
||||||
|
private boolean isHostOnPrefetchWhitelist(String host) {
|
||||||
|
return kPrefetchWhiteList.contains(host);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onNewIntent(Intent intent) {
|
protected void onNewIntent(Intent intent) {
|
||||||
Log.w(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - onNewIntent");
|
Log.w(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - onNewIntent");
|
||||||
@@ -1656,10 +1741,25 @@ abstract public class GeckoApp
|
|||||||
System.exit(0);
|
System.exit(0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (checkLaunchState(LaunchState.Launched)) {
|
||||||
|
Uri data = intent.getData();
|
||||||
|
Bundle bundle = intent.getExtras();
|
||||||
|
// if the intent has data (i.e. a URI to be opened) and the scheme
|
||||||
|
// is either http, we'll prefetch it, which means warming
|
||||||
|
// up the radio and DNS cache by connecting and parsing the redirect
|
||||||
|
// if the return code is between 300 and 400
|
||||||
|
if (data != null &&
|
||||||
|
"http".equals(data.getScheme()) &&
|
||||||
|
(bundle == null || bundle.getInt("prefetched", 0) != 1) &&
|
||||||
|
isHostOnPrefetchWhitelist(data.getHost())) {
|
||||||
|
GeckoAppShell.getHandler().post(new RedirectorRunnable(intent));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
final String action = intent.getAction();
|
final String action = intent.getAction();
|
||||||
if (ACTION_DEBUG.equals(action) &&
|
if (ACTION_DEBUG.equals(action) &&
|
||||||
checkAndSetLaunchState(LaunchState.Launching, LaunchState.WaitForDebugger)) {
|
checkAndSetLaunchState(LaunchState.Launching, LaunchState.WaitForDebugger)) {
|
||||||
|
|
||||||
mMainHandler.postDelayed(new Runnable() {
|
mMainHandler.postDelayed(new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
Log.i(LOGTAG, "Launching from debug intent after 5s wait");
|
Log.i(LOGTAG, "Launching from debug intent after 5s wait");
|
||||||
@@ -1677,8 +1777,12 @@ abstract public class GeckoApp
|
|||||||
Log.i(LOGTAG, "Intent : ACTION_MAIN");
|
Log.i(LOGTAG, "Intent : ACTION_MAIN");
|
||||||
GeckoAppShell.sendEventToGecko(new GeckoEvent(""));
|
GeckoAppShell.sendEventToGecko(new GeckoEvent(""));
|
||||||
}
|
}
|
||||||
|
else if (ACTION_LOAD.equals(action)) {
|
||||||
|
String uri = intent.getDataString();
|
||||||
|
loadUrl(uri, AwesomeBar.Type.EDIT);
|
||||||
|
Log.i(LOGTAG,"onNewIntent: " + uri);
|
||||||
|
}
|
||||||
else if (Intent.ACTION_VIEW.equals(action)) {
|
else if (Intent.ACTION_VIEW.equals(action)) {
|
||||||
prefetchDNS(intent.getData());
|
|
||||||
String uri = intent.getDataString();
|
String uri = intent.getDataString();
|
||||||
GeckoAppShell.sendEventToGecko(new GeckoEvent(uri));
|
GeckoAppShell.sendEventToGecko(new GeckoEvent(uri));
|
||||||
Log.i(LOGTAG,"onNewIntent: " + uri);
|
Log.i(LOGTAG,"onNewIntent: " + uri);
|
||||||
@@ -2334,22 +2438,6 @@ abstract public class GeckoApp
|
|||||||
layerController.setLayerClient(mSoftwareLayerClient);
|
layerController.setLayerClient(mSoftwareLayerClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void prefetchDNS(final Uri u) {
|
|
||||||
// resolving the host here starts up the radio
|
|
||||||
// and may prime the dns cache. See
|
|
||||||
// http://www.stevesouders.com/blog/2011/09/21/making-a-mobile-connection/
|
|
||||||
// for more information.
|
|
||||||
new Thread(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
Log.i(LOGTAG,"resolving: " + u.getHost());
|
|
||||||
InetAddress.getByName(u.getHost());
|
|
||||||
} catch (Exception e) {
|
|
||||||
// we really don't care.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, "DNSPrefetcher Thread").start();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class PluginLayoutParams extends AbsoluteLayout.LayoutParams
|
class PluginLayoutParams extends AbsoluteLayout.LayoutParams
|
||||||
|
|||||||
@@ -151,6 +151,8 @@ ifeq (,$(ANDROID_VERSION_CODE))
|
|||||||
ANDROID_VERSION_CODE=$(shell $(PYTHON) $(topsrcdir)/toolkit/xre/make-platformini.py --print-buildid | cut -c1-10)
|
ANDROID_VERSION_CODE=$(shell $(PYTHON) $(topsrcdir)/toolkit/xre/make-platformini.py --print-buildid | cut -c1-10)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
UA_BUILDID=$(shell $(PYTHON) $(topsrcdir)/toolkit/xre/make-platformini.py --print-buildid | cut -c1-8)
|
||||||
|
|
||||||
DEFINES += \
|
DEFINES += \
|
||||||
-DANDROID_PACKAGE_NAME=$(ANDROID_PACKAGE_NAME) \
|
-DANDROID_PACKAGE_NAME=$(ANDROID_PACKAGE_NAME) \
|
||||||
-DMOZ_APP_DISPLAYNAME="$(MOZ_APP_DISPLAYNAME)" \
|
-DMOZ_APP_DISPLAYNAME="$(MOZ_APP_DISPLAYNAME)" \
|
||||||
@@ -161,6 +163,7 @@ DEFINES += \
|
|||||||
-DMOZ_CRASHREPORTER=$(MOZ_CRASHREPORTER) \
|
-DMOZ_CRASHREPORTER=$(MOZ_CRASHREPORTER) \
|
||||||
-DANDROID_VERSION_CODE=$(ANDROID_VERSION_CODE) \
|
-DANDROID_VERSION_CODE=$(ANDROID_VERSION_CODE) \
|
||||||
-DMOZILLA_OFFICIAL=$(MOZILLA_OFFICIAL) \
|
-DMOZILLA_OFFICIAL=$(MOZILLA_OFFICIAL) \
|
||||||
|
-DUA_BUILDID=$(UA_BUILDID) \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
GARBAGE += \
|
GARBAGE += \
|
||||||
@@ -554,7 +557,7 @@ $(PP_RES_XML): $(subst res/,$(srcdir)/resources/, $(PP_RES_XML).in)
|
|||||||
$(PYTHON) $(topsrcdir)/config/Preprocessor.py \
|
$(PYTHON) $(topsrcdir)/config/Preprocessor.py \
|
||||||
$(AUTOMATION_PPARGS) $(DEFINES) $(ACDEFINES) $< > $@
|
$(AUTOMATION_PPARGS) $(DEFINES) $(ACDEFINES) $< > $@
|
||||||
|
|
||||||
AndroidManifest.xml $(FENNEC_PP_JAVA_FILES) $(SYNC_PP_JAVA_FILES) package-name.txt: % : %.in
|
AndroidManifest.xml $(FENNEC_PP_JAVA_FILES) $(SYNC_PP_JAVA_FILES) package-name.txt: % : %.in Makefile.in
|
||||||
mkdir -p db sync/repositories/android
|
mkdir -p db sync/repositories/android
|
||||||
$(PYTHON) $(topsrcdir)/config/Preprocessor.py \
|
$(PYTHON) $(topsrcdir)/config/Preprocessor.py \
|
||||||
$(AUTOMATION_PPARGS) $(DEFINES) $(ACDEFINES) $< > $@
|
$(AUTOMATION_PPARGS) $(DEFINES) $(ACDEFINES) $< > $@
|
||||||
|
|||||||
Reference in New Issue
Block a user