views:

25

answers:

2

As I've said in other questions, I'm contributing to an open-source browser automation framework (Selenium WebDriver), and having difficulty with IE. The framework uses an external process written in C++, not a Browser Helper Object (BHO). Clicks on elements are generated using Windows messages (WM_LBUTTONDOWN and WM_LBUTTONUP) rather than simulated clicks through COM. Justification for this design decision can be found in the project's wiki here.

In order to determine if a navigation has started, the driver code is attached to the DWebBrowserEvents2::BeforeNavigate2 event. After the click messages are sent to the IE window, however, there is some delay between when SendMessage returns (indicating the IE message loop has processed the message), and when BeforeNavigate2 fires. I suppose I could use attachEvent to attach a callback function to the onclick event of the HTML element and wait for that to be processed, but I'm not sure if that would be synchronous to the BeforeNavigate2 event.

Without calling the Windows Sleep() function for an arbitrary amount of time, how can I wait for the BeforeNavigate2 event after sending a mouse click simulated by Windows messages?

A: 

The navigation code is all asynchronous, so all you can do is sink DWBE2::BeforeNavigate2 and wait for it to fire. You don't have to sleep, just let your message loop run.

There is no way to tell what is going to happen before it happens. There are a lot of simple cases, such as "is it an <a> tag, but there are a near infinite number of ways to navigate IE. One example putting an onclick handler on a random element and then using setTimeout() to navigate the browser in some sneaky way, or navigate based on a random number. Another way is to embed an xml file with an xsl style sheet with a CDATA block in the top that contains javascript that changes window.location via document.write() (seriously—pop-up blocker had to handle this case).

So, basically, you can't tell without doing a lot of analysis up-front (i.e. parse the HTML, javascript, etc).

jeffamaphone
Yes, but the problem is that you can't depend on BeforeNavigate2 firing at all. Consider the case where the element to be clicked on is not a link, but a div, which would not be an uncommon case for a browser automation framework. What I was hoping for was that I could a priori determine whether a navigate would occur, then wait for the event if it would. Sadly, this doesn't appear possible. I'm currently experimenting with attachEvent on the IHTMLElement to make sure I know when the DOM has processed the click, but my event handler isn't being called.
JimEvans
There is no way to tell. I could register an onclick handler that uses setTimeout() to navigate the browser after some arbitrary time period. The only way to tell before the fact what will happen is to completely parse and analyze the HTML and JavaScript, or invent a time machine.
jeffamaphone
A: 

How about having your BeforeNavigate2 event sink raise an event, and the process sending the click message waiting on the event (and some other timeout for safety)?

It doesn't seem to be covered in your description about why you would want to wait, should the next action after the click only happen if the navigation was successful - maybe that should be configurable by the user?

Greg Domjan
Again, you can't depend on the click generating a navigation. It could be that the click just runs a JavaScript function to manipulate the DOM (common in many JS widget frameworks). You'd be waiting the entirety of your timeout for each click that doesn't generate a navigation. The rationale for the wait behavior is that an automated script's next action after a click that navigates is likely targeted to the destination page, thus we must wait for the navigate to complete before continuing.
JimEvans
I would think that in the automation script there would be some way to specify to wait for page load, or to break the script into sections so that the script to run on the newly loaded page was triggered due to the page load and not due to the previous page. To me this seems like it should be some decision made in the script and not left to the design of the click command.
Greg Domjan