Bug 1459301 - 2. Allow nested loopUntilIdle calls; r?snorp
There may be cases where loopUntilIdle calls are nested. Allow these
calls by canceling the previous timeout when we set a new timeout. This
patch also adds some general optimizations to loopUntilIdle, so we don't
create a bunch of objects with every call.
MozReview-Commit-ID: 9glZu8ZTVZ5
--- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/rule/GeckoSessionTestRule.java
+++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/rule/GeckoSessionTestRule.java
@@ -87,21 +87,24 @@ public class GeckoSessionTestRule extend
private static final long DEFAULT_ARM_DEVICE_TIMEOUT_MILLIS = 30000;
private static final long DEFAULT_ARM_EMULATOR_TIMEOUT_MILLIS = 120000;
private static final long DEFAULT_X86_DEVICE_TIMEOUT_MILLIS = 30000;
private static final long DEFAULT_X86_EMULATOR_TIMEOUT_MILLIS = 5000;
private static final long DEFAULT_IDE_DEBUG_TIMEOUT_MILLIS = 86400000;
public static final String APK_URI_PREFIX = "resource://android/";
+ private static final Method sGetNextMessage;
private static final Method sOnPageStop;
private static final Method sOnNewSession;
static {
try {
+ sGetNextMessage = MessageQueue.class.getDeclaredMethod("next");
+ sGetNextMessage.setAccessible(true);
sOnPageStop = GeckoSession.ProgressDelegate.class.getMethod(
"onPageStop", GeckoSession.class, boolean.class);
sOnNewSession = GeckoSession.NavigationDelegate.class.getMethod(
"onNewSession", GeckoSession.class, String.class, GeckoResponse.class);
} catch (final NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
@@ -651,16 +654,47 @@ public class GeckoSessionTestRule extend
}
final HashSet<Class<?>> set = new HashSet<>(list);
return set.toArray(new Class<?>[set.size()]);
}
private static final List<Class<?>> CALLBACK_CLASSES = Arrays.asList(getCallbackClasses());
+ private static final class TimeoutRunnable implements Runnable {
+ private long timeout;
+
+ public void set(final long timeout) {
+ this.timeout = timeout;
+ cancel();
+ HANDLER.postDelayed(this, timeout);
+ }
+
+ public void cancel() {
+ HANDLER.removeCallbacks(this);
+ }
+
+ @Override
+ public void run() {
+ throw new TimeoutException("Timed out after " + timeout + "ms");
+ }
+ }
+
+ /* package */ static final Handler HANDLER = new Handler(Looper.getMainLooper());
+ private static final TimeoutRunnable TIMEOUT_RUNNABLE = new TimeoutRunnable();
+ private static final MessageQueue.IdleHandler IDLE_HANDLER = new MessageQueue.IdleHandler() {
+ @Override
+ public boolean queueIdle() {
+ final Message msg = Message.obtain(HANDLER);
+ msg.obj = HANDLER;
+ HANDLER.sendMessageAtFrontOfQueue(msg);
+ return false; // Remove this idle handler.
+ }
+ };
+
private static GeckoRuntime sRuntime;
private static RDPConnection sRDPConnection;
private static long sLongestWait;
public final Environment env = new Environment();
protected final Instrumentation mInstrumentation =
InstrumentationRegistry.getInstrumentation();
@@ -1114,81 +1148,55 @@ public class GeckoSessionTestRule extend
* timeout is not specified, return immediately. If loop is already idle and timeout is
* specified, wait for a message to arrive first; an exception is thrown if timeout
* expires during the wait.
*
* @param timeout Wait timeout in milliseconds or 0 to not wait.
*/
protected static void loopUntilIdle(final long timeout) {
// Adapted from GeckoThread.pumpMessageLoop.
- final Looper looper = Looper.myLooper();
- final MessageQueue queue = looper.getQueue();
- final Handler handler = new Handler(looper);
- final MessageQueue.IdleHandler idleHandler = new MessageQueue.IdleHandler() {
- @Override
- public boolean queueIdle() {
- final Message msg = Message.obtain(handler);
- msg.obj = handler;
- handler.sendMessageAtFrontOfQueue(msg);
- return false; // Remove this idle handler.
- }
- };
-
- final Method getNextMessage;
- try {
- getNextMessage = queue.getClass().getDeclaredMethod("next");
- } catch (final NoSuchMethodException e) {
- throw new RuntimeException(e);
- }
- getNextMessage.setAccessible(true);
-
- final Runnable timeoutRunnable = new Runnable() {
- @Override
- public void run() {
- throw new TimeoutException("Timed out after " + timeout + "ms");
- }
- };
+ final MessageQueue queue = HANDLER.getLooper().getQueue();
if (timeout > 0) {
- handler.postDelayed(timeoutRunnable, timeout);
+ TIMEOUT_RUNNABLE.set(timeout);
} else {
- queue.addIdleHandler(idleHandler);
+ queue.addIdleHandler(IDLE_HANDLER);
}
final long startTime = SystemClock.uptimeMillis();
try {
while (true) {
final Message msg;
try {
- msg = (Message) getNextMessage.invoke(queue);
+ msg = (Message) sGetNextMessage.invoke(queue);
} catch (final IllegalAccessException | InvocationTargetException e) {
throw unwrapRuntimeException(e);
}
- if (msg.getTarget() == handler && msg.obj == handler) {
+ if (msg.getTarget() == HANDLER && msg.obj == HANDLER) {
// Our idle signal.
break;
} else if (msg.getTarget() == null) {
- looper.quit();
+ HANDLER.getLooper().quit();
return;
}
msg.getTarget().dispatchMessage(msg);
if (timeout > 0) {
- handler.removeCallbacks(timeoutRunnable);
- queue.addIdleHandler(idleHandler);
+ TIMEOUT_RUNNABLE.cancel();
+ queue.addIdleHandler(IDLE_HANDLER);
}
}
final long waitDuration = SystemClock.uptimeMillis() - startTime;
if (waitDuration > sLongestWait) {
sLongestWait = waitDuration;
Log.i(LOGTAG, "New longest wait: " + waitDuration + "ms");
}
} finally {
if (timeout > 0) {
- handler.removeCallbacks(timeoutRunnable);
+ TIMEOUT_RUNNABLE.cancel();
}
}
}
/**
* Wait until a page load has finished on any session. A session must have started a
* page load since the last wait, or this method will wait indefinitely.
*/