GetElementById does not locate elements currently visible in webbrowser - javascript

I'm trying to automate navigation through a webpage that uses HTML and JavaScript. I'm using C# to do this and have not worked with it for a long time so am rather rusty, this is the best I've managed to come up with (including trying webBrowserEvents2).
What I have so far is attached below. Everything runs ok until the 5th while statement which continues as an infinite loop even though element in question is visible in the web browser.
I've commented-out the 5th while statement and everything below it in the first method and then used the second method to list all the elements in the web browser the have IDs. I've found all the elements listed belong to the previous page even though it is no longer visible.
This issue has not been present in the earlier while statements so I am not sure what the problem could be. I have tried refreshing, updating and invalidating the web browser and this has not worked. What is causing this to happen?
//Start navigation
private void goButton_Click(object sender, EventArgs e)
{
//Load Login page
webBrowser1.Navigate(site);
while (webBrowser1.Document.GetElementById("gwt-uid-27") == null)
{
Application.DoEvents();
}
Console.WriteLine("Login page loaded");
//Login
webBrowser1.Document.GetElementById("gwt-uid-27").SetAttribute("value", password);
webBrowser1.Document.GetElementById("gwt-uid-37").InvokeMember("click");
while (webBrowser1.Document.GetElementById("popup_0") == null)
{
Application.DoEvents();
}
while (webBrowser1.Document.GetElementById("popup_0") != null)
{
Application.DoEvents();
}
Console.WriteLine("Login Successful");
//Navigate to timesheets page
webBrowser1.Document.GetElementById("gwt-uid-48").InvokeMember("click");
while (webBrowser1.Document.GetElementById("add-time") == null)
{
Application.DoEvents();
}
//Go to 'Select date' page
webBrowser1.Document.GetElementById("add-time").InvokeMember("click");
while (webBrowser1.Document.GetElementById("gwt-uid-120") == null)
{
Application.DoEvents();
}
//Enter date
string date = String.Format("{0:yyyy-M-d}", dateTimePicker);
webBrowser1.Document.GetElementById("gwt-uid-120").SetAttribute("value", date);
webBrowser1.Document.GetElementById("gwt-uid-125").InvokeMember("click");
}
private void secondary_Click(object sender, EventArgs e)
{
var v = webBrowser1.Document.All;
List<string> c = new List<string>();
foreach (HtmlElement h in v)
{
if (h.Id!=null) c.Add(h.Id);
}
Console.WriteLine("done");
}
Since posting this I've written the same method in VBA with no issues whatsoever and can't figure out why.

Related

How to invoke Eclipse RCP perspective ( Java Class method) from JavaScript code?

I would like to invoke or open Perspective(or Views) from JavaScript function.
I have an Eclipse RCP application, where I have embedded My HTML page. On button click from HTML page, i would like to open Eclipse RCP perspective or View.
I have tried as shown below from javascript function
function populateGeometryData(){
alert('Inside populateGeometryData() function !!!!!');
var geoMetryDataClass = Java.type("com.test.app.ui.view.PageView");
geoMetryDataClass.populateGemetryData("TestVal");
}
Where PageView is one of my perspective. But I got " Uncaught ReferenceError: Java is not defined" .
I think, this error is because of Chrome no longer supports NPAPI (technology required for Java applets). So, is there any other ways to invoke Java class method from JavaScript?
Below is how, I am launching my HTML page from Eclipse RCP code.
#Override
public void createPartControl(Composite parent) {
String user = ExecuteRestService.getLoggedInUser();
System.out.println("Login User "+user);
String html = "/Pages/index.html";
Bundle appBundle = FrameworkUtil.getBundle(this.getClass());
if(appBundle == null) {
System.out.println("bundle is null");
return;
}
URL url = FileLocator.find(appBundle, new Path(html), null);
try {
url = FileLocator.toFileURL(url);
} catch (IOException e) {
// TODO Auto-generated catch block#*
e.printStackTrace();
}
Browser browser = new Browser(parent, SWT.None);
String urlVal = url.toString()+"?LoginUser="+user;
browser.setUrl(urlVal);
}
I am looking some suggestions to call Java Class Method or to Invoke Eclipse RCP perspective from JavaScript function.
Thank you in advance.
UPDATE
As Greg suggested in comments section, using "BrowserFunction" we can call Java class method in Eclipse RCP from javascript if you are using SWT browser to launch your HTML page.
new BrowserFunction(browser, "populateGeometricData")
{
#Override
public Object function(Object[] objects)
{
if(objects.length == 0) {
System.out.println(" array is zero");
}else {
System.out.println("Call from Javascript"
+ objects.length);
System.out.println("Request Id "+ objects[0]);
}
return null;
}
};

Load web page contents in console application using c#

I want to load contents of below web page in console application using c#.
http://justicecourts.maricopa.gov/findacase/casehistory.aspx
Using below code I am getting empty on the screen but it works perfectly if I load google.com web page.
By using WebClient and WebRequest I was getting error "Please enable javascript" and content was not loading so I used below code and javascipt error is not displaying now but web page content is not loading. I am struggling with this issue quite from long time, have seen lot of post regarding this and couldn't get this work.
Could anyone please help?
Thanks in Advance..
class Program
{
private static bool completed = false;
private static WebBrowser wb;
[STAThread]
static void Main(string[] args)
{
wb = new WebBrowser();
wb.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(wb_DocumentCompleted);
wb.Navigate("http://justicecourts.maricopa.gov/findacase/casehistory.aspx");
while (!completed)
{
Application.DoEvents();
Thread.Sleep(100);
}
Console.Write("\n\nDone with it!\n\n");
Console.ReadLine();
}
static void wb_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
Console.WriteLine(wb.Document.Body.InnerHtml);
completed = true;
}
}
If you literally just want to dump the contents of that URL out to the console, try this:
using(WebClient client = new WebClient()) {
Console.WriteLine(client.DownloadString(url));
}
try adding more wait.
static void Main(string[] args)
{
wb = new WebBrowser();
wb.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(wb_DocumentCompleted);
wb.Navigate("http://justicecourts.maricopa.gov/findacase/casehistory.aspx");
while (!completed)
{
Application.DoEvents();
Thread.Sleep(100);
}
//wait even more
for (int i = 0; i < 6; i++)
{
Application.DoEvents();
Thread.Sleep(1000);
}
Console.Write("\n\nDone with it!\n\n");
Console.ReadLine();
}
otherwise you can use EO Browser it is paid. but in your case trail will work cause it is not GUI application.as it shows trail message in GUI.
in EO you can say..
EOContorol.WebView.LoadUrlAndWait(URL);
Try using PhantomJs
basicaly like running a webbrowser without a window. (headless)

C# Webbrowser Programmatically Close JS Confirm Box

while using webbrowser control, I need to programmatically auto close a javascript confirm box.
I used below user32.dll approach and it is working fine on OS which are based english language.
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
But if the computer running non-english OS, it is not working fine as I am using "OK" as text in above method call.
One approach which I suppose can work is I should detect OS language and then use translated "OK" text to use above method.
Here my question is can I change language of the current thread and so webbrowser control so that it show confirm box in English language? This way it would be easy and fast solution in my opinion.
Please suggest your solutions. Thanks in advance.
I am using similar approach in my code however these solutions are working for English language software only. I am actually looking for some generic solution that can run on non-english OS as well.
A possible solution consists in injecting and immediately calling a Javascript function that hijacks the original confirm function:
function hijackConfirm(){
alert('yep!');
window.oldConfirm = window.confirm;
window.confirm = function(){ return true };
}
This is an example in WPF application with the standard WPF WebBrowser control, I'm quite confident that everything I do here can be adjusted to fit the WinForm control (since the underlying ActiveX is the same).
I have a UserControl that acts as an adapter of the WebBrowser, here is the XAML:
<UserControl x:Class="WebBrowserExample.WebBrowserAdapter"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<WebBrowser x:Name="WebBrowserControl"></WebBrowser>
</Grid>
</UserControl>
First, in the WebBrowserAdapter class, you need a method to inject a javascript function in the current HTML document:
public void InjectScript(String scriptText)
{
HTMLDocument htmlDocument = (HTMLDocument)WebBrowserControl.Document;
var headElements = htmlDocument.getElementsByTagName("head");
if (headElements.length == 0)
{
throw new IndexOutOfRangeException("No element with tag 'head' has been found in the document");
}
var headElement = headElements.item(0);
IHTMLScriptElement script = (IHTMLScriptElement)htmlDocument.createElement("script");
script.text = scriptText;
headElement.AppendChild(script);
}
then you call InjectScript, when needed, whenever a document completes to load:
void WebBrowserAdapter_Loaded(object sender, RoutedEventArgs e)
{
WebBrowserControl.LoadCompleted += WebBrowserControl_LoadCompleted;
WebBrowserControl.Navigate("http://localhost:9080/console/page.html");
}
void WebBrowserControl_LoadCompleted(object sender, NavigationEventArgs e)
{
//HookHTMLElements();
String script =
#" function hijackConfirm(){
alert('yep!');
window.oldConfirm = window.confirm;
window.confirm = function(){ return true };
}";
InjectScript(script);
WebBrowserControl.InvokeScript("hijackConfirm");
}
Here I navigate to http://localhost:9080/console/page.html, which is a test page hosted on my system. This works well in this simple scenario. If you find this could apply to you, you may need to tweak a little bit the code. In order to compile the code, you have to add Microsoft.mshtml in the project references
EDIT: WinForm version
To make it work, you have to use the IE 11 engine in your application. Follow the instructions found here to set it
I just tried a WinForm version of this and it works with some minor changes. Here is the code of a form that has a WebBrowser control as one of its children:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.Load += Form1_Load;
}
void Form1_Load(object sender, EventArgs e)
{
webBrowserControl.Navigate("file:///C:/Temp/page.html");
webBrowserControl.Navigated += webBrowserControl_Navigated;
}
void webBrowserControl_Navigated(object sender, WebBrowserNavigatedEventArgs e)
{
InjectConfirmHijack();
}
private void InjectConfirmHijack()
{
String script =
#" function hijackConfirm(){
alert('yep!');
window.oldConfirm = window.confirm;
window.confirm = function(){ return true };
}";
InjectScript(script);
webBrowserControl.Document.InvokeScript("hijackConfirm");
}
public void InjectScript(String scriptText)
{
//mshtml.HTMLDocument htmlDocument = (mshtml.IHTMLDocument) webBrowserControl.Document.get;
var headElements = webBrowserControl.Document.GetElementsByTagName("head");
if (headElements.Count == 0)
{
throw new IndexOutOfRangeException("No element with tag 'head' has been found in the document");
}
var headElement = headElements[0];
var script = webBrowserControl.Document.CreateElement("script");
script.InnerHtml = scriptText;
headElement.AppendChild(script);
}
}

Suppress Script errors in WebBrowser control but still allow popups

I am getting vaious script errors when I use a WebBrowser Control to view web pages in my WPF application. To supress these I used the 'Silent' method as outlined in this link: http://social.msdn.microsoft.com/Forums/vstudio/en-US/4f686de1-8884-4a8d-8ec5-ae4eff8ce6db/new-wpf-webbrowser-how-do-i-suppress-script-errors?forum=wpf
void wbMain_Navigated(object sender, NavigationEventArgs e)
{
SetSilent(WebBrowser, true);
}
public static void SetSilent(WebBrowser browser, bool silent)
{
if (browser == null)
throw new ArgumentNullException("browser");
FieldInfo webBrowserInfo = browser.GetType().GetField("_axIWebBrowser2", BindingFlags.Instance | BindingFlags.NonPublic);
object comWebBrowser = null;
if (webBrowserInfo != null)
comWebBrowser = webBrowserInfo.GetValue(browser);
if (comWebBrowser != null)
comWebBrowser.GetType().InvokeMember("Silent", BindingFlags.SetProperty, null, comWebBrowser, new object[] { silent });
}
But the problem now is that because the "Silent" property has been set none of my web pages right click options work. (There are many right click options on my flash website that pop up various menus).
If I don't use the workaround above then the right click action brings up script error:
"An error has occurred in the script on this page"
How can I get around this?

Disabling caching, cookies and everything else in a WebView

I have a webservice that I am trying to authenticate with in the background using a webview. When I initially send the request it will work appropriately (failure/success based on credentials), but after it seems like I am getting a cached response.
Here is my webview setup code:
WebView browser = new WebView(this);
WebSettings settings = browser.getSettings();
settings.setJavaScriptEnabled(true);
settings.setSavePassword(false);
settings.setCacheMode(WebSettings.LOAD_NO_CACHE);
settings.setAppCacheEnabled(false);
browser.setWebChromeClient(new WebChromeClient() {
public void onProgressChanged(WebView view, int progress) {
Log.d("BROWSERPROGRESS", Integer.toString(progress));
}
});
jsInterface = new AddAccountJSInterface();
browser.addJavascriptInterface(jsInterface, "ADDACCOUNTJSINTERFACE");
browser.setWebViewClient(new AddAccountClient(this));
So as you may see I have two additional classes controlling my webView:
An object that provides an interface for javascript (AddAccountJSInterface)
A WebViewClient
Additionally I do have a WebChromeClient, but it's only there for debugging and I'm pretty sure that it won't interfere with anything.
The JS interface simply provides an easy way of getting the body HTML for performing analysis, so I'm confident that isn't the issue either.
The WebViewClient has the following code in it which does most of the "custom" work for routing based on various responses from the webservice.
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if(url.contains(INSTALL_PREFIX)) {
HashMap<String, String> params = extractParameters(url);
verificationComplete(params);
return true;
}
return false;
}
#Override
public void onPageFinished(WebView view, String url){
if(invalidShop(view)) {
Toast.makeText(context, context.getString(R.string.no_find_shop), Toast.LENGTH_SHORT).show();
shopAddressField.requestFocus();
replaceUiElements(loadingBar, addAccountButton);
} else if(url.contains(ADMIN_AUTH_LOGIN)) {
if(invalidLogin(view)) {
Toast.makeText(context, context.getString(R.string.invalid_login),Toast.LENGTH_SHORT).show();
emailField.requestFocus();
replaceUiElements(loadingBar, addAccountButton);
} else {
String email = emailField.getText().toString();
String password = passwordField.getText().toString();
String submitJS = String.format(FORM_SUBMISSION_JS, email, password);
jsInterface.setInnerHTML("");
browser.loadUrl(submitJS);
}
}
}
In my activity I have 3 text fields that I need to fill followed by clicking a button to submit it. The activity then takes in the data from 3 text fields (shopAddressField, usernameField, passwordField) and then executes some javascript that populates some form data (which was loaded in the invisible webView) then clicks the submit button.
It is the last part that is messing up, which appears to be caching the response from the server (perhaps using cookies?) and return that instead of asking the server if the data is correct or not.
A bit of clarification:
JSInterface is simply a Java object that allows me to execute javascript on my webview which is tied to a function within that object. In my case my JSInterface has one function which is setInnerHtml(String html).
This is the javascript that is executed on the webview:
javascript:window.ADDACOUNTJSINTERFACE.setInnerHTML(document.body.innerHTML)
And this is the setInnerHtml function:
public void setInnerHtml(String innerHtml) {
this.innerHtml = innerHtml;
}
So when I actually execute jsInterface.setInnerHtml("") I'm just over-writing the HTML that was pulled in (to be sure I'm not getting my old data from there for some reason).
As for my submitJS it is once again some Javascript that is executed on my webView as follows:
// submitJS will be something like this once all the credentials have been set
// Note: I know that the server will make jQuery available
// Note: Much of the Java string formatting has been removed to help clarify
// the code.
String submitJS =
"javascript:(function() {
$('login-input').value='username';
$('password').value='password';
$('sign-in-form').up().submit();
})()"
// I then simply get the webview to execute the javascript above
webView.loadData(submitJS);
So it turns out the problem wasn't based around the Caching, and possibly not cookies.
When executing javascript on your webView it does this in a separate thread and can be quite slow. This lead to a race condition which caused code to be executed in the wrong order.
I've solved this problem by using a Semaphore as a Mutex. This allows me to prevent my getter from returning before the Javascript on the webView is able to execute.
The interface I created now looks like this:
private class AddAccountJSInterface {
private final String TAG = getClass().getName().toUpperCase();
private Semaphore mutex = new Semaphore(1, false);
private String innerHTML;
public void aquireSemaphore() {
Log.d(TAG, "Attempting to lock semaphore");
try {
mutex.acquire();
} catch(InterruptedException e) {
Log.d(TAG, "Oh snap, we got interrupted. Just going to abort.");
return;
}
Log.d(TAG, "Semaphore has been aquired");
}
#SuppressWarnings("unused")
public void setInnerHTML(String html) {
this.innerHTML = html;
Log.d(TAG, "setInnerHTML is now releasing semaphore.");
mutex.release();
Log.d(TAG, "setInnerHTML has successfully released the semaphore.");
}
public synchronized String getInnerHTML() {
Log.d(TAG, "getInnerHTML attempting to aquire semaphore, may block...");
String innerHTML = "";
try {
mutex.acquire();
Log.d(TAG, "getInnerHTML has aquired the semaphore, grabbing data.");
innerHTML = this.innerHTML;
Log.d(TAG, "getInnerHTML no longer needs semaphore, releasing");
mutex.release();
} catch (InterruptedException e) {
Log.d(TAG, "Something has gone wrong while attempting to aquire semaphore, aborting");
}
return innerHTML;
}
}
Now the way I use this in my code is as follows:
// I have access to the jsInterface object which is an instance of the class above as well as a webView which I will be executing the javascript on.
String getInnerHtmlJS = "javascript:window.MYJSINTERFACE.setInnerHTML(document.body.innerHTML);"
jsInterface.aquireSemaphore()
// Execute my JS on the webview
jsInterface.loadUrl(getInnerHtmlJS)
// Now we get our inner HTML
// Note: getInnerHTML will block since it must wait for the setInnerHTML (executed via the JS) function to release the semaphore
String theInnerHTML = jsInterface.getInnerHTML();

Categories