Index: chrome/test/android/javatests/src/org/chromium/chrome/test/util/ChromeTabUtils.java |
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/ChromeTabUtils.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/ChromeTabUtils.java |
index 9422cdfc06b732b74670bd26ddefce591f7fb0d2..078e566913d045da6e01c2cfc7c6c79b325275e1 100644 |
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/ChromeTabUtils.java |
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/ChromeTabUtils.java |
@@ -20,6 +20,7 @@ import org.chromium.chrome.browser.compositor.layouts.components.CompositorButto |
import org.chromium.chrome.browser.compositor.overlays.strip.StripLayoutHelper; |
import org.chromium.chrome.browser.tab.EmptyTabObserver; |
import org.chromium.chrome.browser.tab.Tab; |
+import org.chromium.chrome.browser.tab.TabObserver; |
import org.chromium.chrome.browser.tabmodel.EmptyTabModelObserver; |
import org.chromium.chrome.browser.tabmodel.TabModel; |
import org.chromium.chrome.browser.tabmodel.TabModel.TabLaunchType; |
@@ -33,6 +34,7 @@ import org.chromium.ui.base.DeviceFormFactor; |
import java.util.List; |
import java.util.concurrent.Callable; |
+import java.util.concurrent.CountDownLatch; |
import java.util.concurrent.TimeUnit; |
import java.util.concurrent.TimeoutException; |
@@ -40,10 +42,70 @@ import java.util.concurrent.TimeoutException; |
* A utility class that contains methods generic to all Tabs tests. |
*/ |
public class ChromeTabUtils { |
- private static final String TAG = "ChromeTabUtils"; |
+ private static final String TAG = "cr_ChromeTabUtils"; |
/** |
- * Waits for the given tab to finish loading it's current page. |
+ * An observer that waits for a Tab to load a page. |
+ * |
+ * The observer can be configured to either wait for the Tab to load a specific page |
+ * (if expectedUrl is non-null) or any page (otherwise). On seeing the tab finish |
+ * a page load or crash, the observer will notify the provided callback and stop |
+ * watching the tab. On load stop, the observer will decrement the provided latch |
+ * and continue watching the page in case the tab subsequently crashes or finishes |
+ * a page load. |
+ * |
+ * This may seem complicated, but it's intended to handle three distinct cases: |
+ * 1) Successful page load + observer starts watching before onPageLoadFinished fires. |
+ * This is the most normal case: onPageLoadFinished fires, then onLoadStopped fires, |
+ * and we see both. |
+ * 2) Crash on page load. onLoadStopped fires, then onCrash fires, and we see both. |
+ * 3) Successful page load + observer starts watching after onPageLoadFinished fires. |
+ * We miss the onPageLoadFinished and *only* see onLoadStopped. |
+ * |
+ * Receiving onPageLoadFinished is sufficient to know that we're dealing with scenario #1. |
+ * Receiving onCrash is sufficient to know that we're dealing with scenario #2. |
+ * Receiving onLoadStopped without a preceding onPageLoadFinished indicates that we're dealing |
+ * with either scenario #2 *or* #3, so we have to keep watching for a call to onCrash. |
+ */ |
+ private static class TabPageLoadedObserver extends EmptyTabObserver { |
+ private CallbackHelper mCallback; |
+ private String mExpectedUrl; |
+ private CountDownLatch mLoadStoppedLatch; |
+ |
+ public TabPageLoadedObserver(CallbackHelper loadCompleteCallback, String expectedUrl, |
+ CountDownLatch loadStoppedLatch) { |
+ mCallback = loadCompleteCallback; |
+ mExpectedUrl = expectedUrl; |
+ mLoadStoppedLatch = loadStoppedLatch; |
+ } |
+ |
+ @Override |
+ public void onCrash(Tab tab, boolean sadTabShown) { |
+ mCallback.notifyFailed("Tab crashed :("); |
+ tab.removeObserver(this); |
+ } |
+ |
+ @Override |
+ public void onLoadStopped(Tab tab, boolean toDifferentDocument) { |
+ mLoadStoppedLatch.countDown(); |
+ } |
+ |
+ @Override |
+ public void onPageLoadFinished(Tab tab) { |
+ if (mExpectedUrl == null || TextUtils.equals(tab.getUrl(), mExpectedUrl)) { |
+ mCallback.notifyCalled(); |
+ tab.removeObserver(this); |
+ } |
+ } |
+ } |
+ |
+ private static boolean loadComplete(Tab tab, String url) { |
+ return !tab.isLoading() && (url == null || TextUtils.equals(tab.getUrl(), url)) |
+ && !tab.getWebContents().isLoadingToDifferentDocument(); |
+ } |
+ |
+ /** |
+ * Waits for the given tab to finish loading its current page. |
* |
* @param tab The tab to wait for the page loading to be complete. |
* @param url The URL that will be waited to load for. Pass in null if loading the |
@@ -53,44 +115,47 @@ public class ChromeTabUtils { |
throws InterruptedException { |
Assert.assertFalse(ThreadUtils.runningOnUiThread()); |
- final boolean checkUrl = url != null; |
- |
+ final CountDownLatch loadStoppedLatch = new CountDownLatch(1); |
final CallbackHelper loadedCallback = new CallbackHelper(); |
ThreadUtils.runOnUiThreadBlocking(new Runnable() { |
@Override |
public void run() { |
- if (!tab.isLoading() |
- && (!checkUrl || TextUtils.equals(tab.getUrl(), url)) |
- && !tab.getWebContents().isLoadingToDifferentDocument()) { |
+ if (loadComplete(tab, url)) { |
loadedCallback.notifyCalled(); |
return; |
} |
- |
- tab.addObserver(new EmptyTabObserver() { |
- @Override |
- public void onPageLoadFinished(Tab tab) { |
- if (!checkUrl || TextUtils.equals(tab.getUrl(), url)) { |
- loadedCallback.notifyCalled(); |
- tab.removeObserver(this); |
- } |
- } |
- }); |
+ tab.addObserver(new TabPageLoadedObserver(loadedCallback, url, loadStoppedLatch)); |
} |
}); |
try { |
loadedCallback.waitForCallback(0); |
} catch (TimeoutException e) { |
- Assert.fail("Page did not load. Tab information at time of failure --" |
- + " url: " + url |
- + ", final URL: " + tab.getUrl() |
- + ", load progress: " + tab.getProgress() |
- + ", is loading: " + Boolean.toString(tab.isLoading())); |
+ // In the event that: |
+ // 1) the tab is on the correct page |
+ // 2) we weren't notified that the page load finished |
+ // 3) we *were* notified that the tab stopped loading |
+ // 4) the tab didn't crash |
+ // |
+ // then it's likely the case that we started observing the tab after |
+ // onPageLoadFinished but before onLoadStopped. (The latter sets tab.mIsLoading to |
+ // false.) Try to carry on with the test. |
+ if (loadStoppedLatch.getCount() == 0 && loadComplete(tab, url)) { |
+ Log.w(TAG, |
+ "onPageLoadFinished was never called, but loading stopped " |
+ + "on the expected page. Tentatively continuing."); |
+ } else { |
+ Assert.fail("Page did not load. Tab information at time of failure --" |
+ + " url: " + url + ", final URL: " + tab.getUrl() + ", load progress: " |
+ + tab.getProgress() + ", is loading: " + Boolean.toString(tab.isLoading()) |
+ + ", web contents loading: " |
+ + Boolean.toString(tab.getWebContents().isLoadingToDifferentDocument())); |
+ } |
} |
} |
/** |
- * Waits for the given tab to finish loading it's current page. |
+ * Waits for the given tab to finish loading its current page. |
* |
* @param tab The tab to wait for the page loading to be complete. |
* @param loadTrigger The trigger action that will result in a page load finished event |
@@ -102,7 +167,7 @@ public class ChromeTabUtils { |
} |
/** |
- * Waits for the given tab to finish loading it's current page. |
+ * Waits for the given tab to finish loading its current page. |
* |
* @param tab The tab to wait for the page loading to be complete. |
* @param loadTrigger The trigger action that will result in a page load finished event |
@@ -112,17 +177,14 @@ public class ChromeTabUtils { |
public static void waitForTabPageLoaded( |
final Tab tab, Runnable loadTrigger, long secondsToWait) |
throws InterruptedException { |
+ final CountDownLatch countDownLatch = new CountDownLatch(1); |
final CallbackHelper loadedCallback = new CallbackHelper(); |
ThreadUtils.runOnUiThreadBlocking(new Runnable() { |
@Override |
public void run() { |
- tab.addObserver(new EmptyTabObserver() { |
- @Override |
- public void onPageLoadFinished(Tab tab) { |
- loadedCallback.notifyCalled(); |
- tab.removeObserver(this); |
- } |
- }); |
+ TabObserver observer = |
+ new TabPageLoadedObserver(loadedCallback, null, countDownLatch); |
+ tab.addObserver(observer); |
} |
}); |
loadTrigger.run(); |
@@ -130,14 +192,15 @@ public class ChromeTabUtils { |
loadedCallback.waitForCallback(0, 1, secondsToWait, TimeUnit.SECONDS); |
} catch (TimeoutException e) { |
Assert.fail("Page did not load. Tab information at time of failure --" |
- + " url: " + tab.getUrl() |
- + ", load progress: " + tab.getProgress() |
- + ", is loading: " + Boolean.toString(tab.isLoading())); |
+ + " url: " + tab.getUrl() + ", load progress: " + tab.getProgress() |
+ + ", is loading: " + Boolean.toString(tab.isLoading()) |
+ + ", web contents loading: " |
+ + Boolean.toString(tab.getWebContents().isLoadingToDifferentDocument())); |
} |
} |
/** |
- * Waits for the given tab to start loading it's current page. |
+ * Waits for the given tab to start loading its current page. |
* |
* @param tab The tab to wait for the page loading to be started. |
* @param loadTrigger The trigger action that will result in a page load started event |