From: <mgu...@us...> - 2013-06-12 14:00:30
|
Revision: 8334 http://sourceforge.net/p/htmlunit/code/8334 Author: mguillem Date: 2013-06-12 14:00:27 +0000 (Wed, 12 Jun 2013) Log Message: ----------- waitForBackgroundJavaScript: handle ugly case where the frame window with the background job is closed due to the job Modified Paths: -------------- trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/WebClient.java trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/WebClientWaitForBackgroundJobsTest.java Modified: trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/WebClient.java =================================================================== --- trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/WebClient.java 2013-06-11 16:28:05 UTC (rev 8333) +++ trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/WebClient.java 2013-06-12 14:00:27 UTC (rev 8334) @@ -66,6 +66,7 @@ import com.gargoylesoftware.htmlunit.javascript.JavaScriptEngine; import com.gargoylesoftware.htmlunit.javascript.JavaScriptErrorListener; import com.gargoylesoftware.htmlunit.javascript.ProxyAutoConfig; +import com.gargoylesoftware.htmlunit.javascript.background.JavaScriptJobManager; import com.gargoylesoftware.htmlunit.javascript.host.Event; import com.gargoylesoftware.htmlunit.javascript.host.Location; import com.gargoylesoftware.htmlunit.javascript.host.Node; @@ -146,6 +147,8 @@ private final Set<WebWindowListener> webWindowListeners_ = new HashSet<WebWindowListener>(5); private final Stack<TopLevelWindow> topLevelWindows_ = new Stack<TopLevelWindow>(); // top-level windows private final List<WebWindow> windows_ = Collections.synchronizedList(new ArrayList<WebWindow>()); // all windows + private transient List<WeakReference<JavaScriptJobManager>> jobManagers_ = + Collections.synchronizedList(new ArrayList<WeakReference<JavaScriptJobManager>>()); private WebWindow currentWindow_; private HTMLParserListener htmlParserListener_; @@ -1004,6 +1007,8 @@ public void registerWebWindow(final WebWindow webWindow) { WebAssert.notNull("webWindow", webWindow); windows_.add(webWindow); + // register JobManager here but don't deregister in deregisterWebWindow as it can live longer + jobManagers_.add(new WeakReference<JavaScriptJobManager>(webWindow.getJobManager())); } /** @@ -1736,18 +1741,25 @@ public int waitForBackgroundJavaScript(final long timeoutMillis) { int count = 0; final long endTime = System.currentTimeMillis() + timeoutMillis; - for (Iterator<WebWindow> i = windows_.iterator(); i.hasNext();) { - final WebWindow window; + for (Iterator<WeakReference<JavaScriptJobManager>> i = jobManagers_.iterator(); i.hasNext();) { + final JavaScriptJobManager jobManager; + final WeakReference<JavaScriptJobManager> reference; try { - window = i.next(); + reference = i.next(); + jobManager = reference.get(); + if (jobManager == null) { + i.remove(); + continue; + } } catch (final ConcurrentModificationException e) { - i = windows_.iterator(); + i = jobManagers_.iterator(); count = 0; continue; } + final long newTimeout = endTime - System.currentTimeMillis(); - count += window.getJobManager().waitForJobs(newTimeout); + count += jobManager.waitForJobs(newTimeout); } if (count != getAggregateJobCount()) { final long newTimeout = endTime - System.currentTimeMillis(); @@ -1783,18 +1795,24 @@ public int waitForBackgroundJavaScriptStartingBefore(final long delayMillis) { int count = 0; final long endTime = System.currentTimeMillis() + delayMillis; - for (Iterator<WebWindow> i = windows_.iterator(); i.hasNext();) { - final WebWindow window; + for (Iterator<WeakReference<JavaScriptJobManager>> i = jobManagers_.iterator(); i.hasNext();) { + final JavaScriptJobManager jobManager; + final WeakReference<JavaScriptJobManager> reference; try { - window = i.next(); + reference = i.next(); + jobManager = reference.get(); + if (jobManager == null) { + i.remove(); + continue; + } } catch (final ConcurrentModificationException e) { - i = windows_.iterator(); + i = jobManagers_.iterator(); count = 0; continue; } final long newDelay = endTime - System.currentTimeMillis(); - count += window.getJobManager().waitForJobsStartingBefore(newDelay); + count += jobManager.waitForJobsStartingBefore(newDelay); } if (count != getAggregateJobCount()) { final long newDelay = endTime - System.currentTimeMillis(); @@ -1809,17 +1827,24 @@ */ private int getAggregateJobCount() { int count = 0; - for (Iterator<WebWindow> i = windows_.iterator(); i.hasNext();) { - final WebWindow window; + for (Iterator<WeakReference<JavaScriptJobManager>> i = jobManagers_.iterator(); i.hasNext();) { + final JavaScriptJobManager jobManager; + final WeakReference<JavaScriptJobManager> reference; try { - window = i.next(); + reference = i.next(); + jobManager = reference.get(); + if (jobManager == null) { + i.remove(); + continue; + } } catch (final ConcurrentModificationException e) { - i = windows_.iterator(); + i = jobManagers_.iterator(); count = 0; continue; } - count += window.getJobManager().getJobCount(); + final int jobCount = jobManager.getJobCount(); + count += jobCount; } return count; } @@ -1831,6 +1856,7 @@ in.defaultReadObject(); webConnection_ = createWebConnection(); scriptEngine_ = new JavaScriptEngine(this); + jobManagers_ = Collections.synchronizedList(new ArrayList<WeakReference<JavaScriptJobManager>>()); } private WebConnection createWebConnection() { Modified: trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/WebClientWaitForBackgroundJobsTest.java =================================================================== --- trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/WebClientWaitForBackgroundJobsTest.java 2013-06-11 16:28:05 UTC (rev 8333) +++ trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/WebClientWaitForBackgroundJobsTest.java 2013-06-12 14:00:27 UTC (rev 8334) @@ -17,6 +17,7 @@ import static org.junit.Assert.assertNotNull; import java.io.IOException; +import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -547,6 +548,62 @@ noOfJobs = client.waitForBackgroundJavaScriptStartingBefore(500); assertTrue(noOfJobs == 1 || noOfJobs == 2); // maybe one is running } + + /** + * Methods waitForBackgroundJavaScript[StartingBefore] should not look for running jobs only on the existing + * windows but as well on the ones that have been (freshly) closed. + * This test shows the case where a background job in a frame causes the window of this frame to be unregistered + * by the WebClient but this job should still be considered until it completes. + * @throws Exception if the test fails + */ + @Test + public void jobsFromAClosedWindowShouldntBeIgnore() throws Exception { + final String html = "<html><head><title>page 1</title></head>\n" + + "<body>\n" + + "<iframe src='iframe.html'></iframe>\n" + + "</body></html>"; + + final String iframe = "<html><body>\n" + + "<script>\n" + + "setTimeout(function() { parent.location = '/page3.html'; }, 50);\n" + + "</script>\n" + + "</body></html>"; + final String page3 = "<html><body><script>\n" + + "parent.location = '/delayedPage4.html';\n" + + "</script></body></html>"; + + final WebClient client = getWebClient(); + + final ThreadSynchronizer threadSynchronizer = new ThreadSynchronizer(); + + final MockWebConnection webConnection = new MockWebConnection() { + @Override + public WebResponse getResponse(final WebRequest request) throws IOException { + if (request.getUrl().toExternalForm().endsWith("/delayedPage4.html")) { + threadSynchronizer.setState("/delayedPage4.html requested"); + threadSynchronizer.waitForState("ready to call waitForBGJS"); + threadSynchronizer.sleep(1000); + } + return super.getResponse(request); + } + }; + + webConnection.setDefaultResponse(html); + webConnection.setResponse(new URL(getDefaultUrl(), "iframe.html"), iframe); + webConnection.setResponse(new URL(getDefaultUrl(), "page3.html"), page3); + webConnection.setResponseAsGenericHtml(new URL(getDefaultUrl(), "delayedPage4.html"), "page 4"); + client.setWebConnection(webConnection); + + client.getPage(getDefaultUrl()); + + threadSynchronizer.waitForState("/delayedPage4.html requested"); + threadSynchronizer.setState("ready to call waitForBGJS"); + final int noOfJobs = client.waitForBackgroundJavaScriptStartingBefore(500); + assertEquals(0, noOfJobs); + + final HtmlPage page = (HtmlPage) client.getCurrentWindow().getEnclosedPage(); + assertEquals("page 4", page.getTitleText()); + } } /** |