fix sendKeys function for file-upload inputs #66

Closed
wants to merge 5 commits into
from
View
BIN lib/jna.jar
Binary file not shown.
View
BIN lib/platform.jar
Binary file not shown.
View
2 maven/pom.xml
@@ -11,7 +11,7 @@
<groupId>com.opera</groupId>
<artifactId>operadriver</artifactId>
<packaging>jar</packaging>
- <version>0.11</version>
+ <version>0.11-wladich</version>
<name>OperaDriver</name>
<description>OperaDriver is a vendor-supported WebDriver implementation developed by Opera Software and volunteers that implements WebDriver's wire protocol.</description>
<url>http://opera.com/developer/tools/operadriver/</url>
View
100 src/com/opera/core/systems/OperaWebElement.java
@@ -51,6 +51,7 @@
import java.util.concurrent.Callable;
import java.util.logging.Logger;
+import com.opera.core.systems.util.FileSendKeys;
/**
* Implements WebDriver's {@link WebElement}, but also extends it with Opera specific methods.
*/
@@ -244,9 +245,15 @@ public void sendKeys(CharSequence... keysToSend) {
// Keys that have been held down, and need to be released
ArrayList<String> heldKeys = new ArrayList<String>();
- if (getTagName().equalsIgnoreCase("input")
- && (hasAttribute("type") && getAttribute("type").equals("file"))) {
+ boolean fileInput = getTagName().equalsIgnoreCase("input")
+ && (hasAttribute("type") && getAttribute("type").equals("file"));
+ if (fileInput) {
click();
+ StringBuffer buf = new StringBuffer();
+ for (CharSequence seq : keysToSend) {
+ buf.append(seq);
+ }
+ new FileSendKeys(parent).SendKeysFileDialog(buf.toString());
} else {
executeMethod("locator.focus()");
// When focused textareas return the cursor to the last position it was at. Inputs place the
@@ -268,42 +275,18 @@ public void sendKeys(CharSequence... keysToSend) {
}
}
- // This code is a bit ugly. Because "special" keys can be sent either as an individual
- // argument, or in the middle of a string of "normal" characters, we have to loop through the
- // string and check each against a list of special keys.
-
- parent.getScopeServices().captureOperaIdle();
- for (CharSequence seq : keysToSend) {
- if (seq instanceof Keys) {
- String key = OperaKeys.get(((Keys) seq).name());
- // Check if this is a key we hold down, and haven't already pressed, and press, but don't
- // release it. That's done at the end of this method.
- if (holdKeys.contains(key) && !heldKeys.contains(key) && !execService.keyIsPressed(key)) {
- execService.key(key, false);
- heldKeys.add(key);
- } else if (key.equals("null")) {
- for (String hkey : heldKeys) {
- execService.key(hkey, true);
- }
- } else {
- execService.key(key);
- }
- } else if (seq.toString().equals("\n")) {
- execService.key("enter");
- } else {
- // We need to check each character to see if it is a "special" key
- for (int i = 0; i < seq.length(); i++) {
- Character c = seq.charAt(i);
- String keyName = charToKeyName(c);
-
- // Buffer normal keys for a single type() call
- if (keyName == null) {
- execService.type(c.toString());
- } else {
- String key = OperaKeys.get(keyName);
- // TODO: Code repeated from above
- if (holdKeys.contains(key) && !heldKeys.contains(key) && !execService
- .keyIsPressed(key)) {
+ if (!fileInput){
+ // This code is a bit ugly. Because "special" keys can be sent either as an individual
+ // argument, or in the middle of a string of "normal" characters, we have to loop through the
+ // string and check each against a list of special keys.
+
+ parent.getScopeServices().captureOperaIdle();
+ for (CharSequence seq : keysToSend) {
+ if (seq instanceof Keys) {
+ String key = OperaKeys.get(((Keys) seq).name());
+ // Check if this is a key we hold down, and haven't already pressed, and press, but don't
+ // release it. That's done at the end of this method.
+ if (holdKeys.contains(key) && !heldKeys.contains(key) && !execService.keyIsPressed(key)) {
execService.key(key, false);
heldKeys.add(key);
} else if (key.equals("null")) {
@@ -313,17 +296,42 @@ public void sendKeys(CharSequence... keysToSend) {
} else {
execService.key(key);
}
+ } else if (seq.toString().equals("\n")) {
+ execService.key("enter");
+ } else {
+ // We need to check each character to see if it is a "special" key
+ for (int i = 0; i < seq.length(); i++) {
+ Character c = seq.charAt(i);
+ String keyName = charToKeyName(c);
+
+ // Buffer normal keys for a single type() call
+ if (keyName == null) {
+ execService.type(c.toString());
+ } else {
+ String key = OperaKeys.get(keyName);
+ // TODO: Code repeated from above
+ if (holdKeys.contains(key) && !heldKeys.contains(key) && !execService
+ .keyIsPressed(key)) {
+ execService.key(key, false);
+ heldKeys.add(key);
+ } else if (key.equals("null")) {
+ for (String hkey : heldKeys) {
+ execService.key(hkey, true);
+ }
+ } else {
+ execService.key(key);
+ }
+ }
+ }
}
}
- }
- }
- if (heldKeys.size() > 0) {
- for (String key : heldKeys) {
- execService.key(key, true);
- }
+ if (heldKeys.size() > 0) {
+ for (String key : heldKeys) {
+ execService.key(key, true);
+ }
+ }
}
-
try {
parent.waitForLoadToComplete();
} catch (ResponseNotReceivedException e) {
@@ -745,4 +753,4 @@ private void assertElementNotStale() {
}
}
-}
+}
View
114 src/com/opera/core/systems/util/FileSendKeys.java
@@ -0,0 +1,114 @@
+package com.opera.core.systems.util;
+
+import com.opera.core.systems.OperaDriver;
+import com.sun.jna.Pointer;
+import com.sun.jna.platform.win32.User32;
+import com.sun.jna.Native;
+import com.sun.jna.Platform;
+import com.sun.jna.platform.win32.WinDef.HWND;
+import com.sun.jna.ptr.IntByReference;
+import com.sun.jna.win32.StdCallLibrary;
+import java.util.ArrayList;
+
+public class FileSendKeys {
+ private interface MyUser32 extends StdCallLibrary {
+ public static final int WM_SETTEXT = 0xc;
+ public static final int WM_LBUTTONDOWN= 0x201;
+ public static final int WM_LBUTTONUP = 0x202;
+ public static final int IDOK = 1;
+ public final MyUser32 INSTANCE = Platform.isWindows() ? ((MyUser32) Native.loadLibrary("user32", MyUser32.class)):null;
+ public int SendMessageW(HWND hwnd, int i, int wparam, char[] lparam);
+ public HWND GetDlgItem(HWND hDlg, int nIDDlgItem);
+ }
+
+
+ private static String b2s(char b[]) {
+ // Converts C string to Java String
+ int len = 0;
+ while (b[len] != 0)
+ ++len;
+ return new String(b, 0, len);
+ }
+
+ private int operaPID = 0;
+
+ public FileSendKeys(OperaDriver operaDriver){
+ operaPID = operaDriver.utils().getPID();
+ }
+
+ private HWND FindOperaOpenDialog(final int pid) {
+ final ArrayList<HWND> dialogHandle = new ArrayList<HWND>();
+ User32.INSTANCE.EnumWindows(new User32.WNDENUMPROC() {
+ @Override
+ public boolean callback(HWND hwnd, Pointer pntr) {
+ IntByReference windowPid = new IntByReference();
+ char[] windowClass = new char[100];
+ User32.INSTANCE.GetWindowThreadProcessId(hwnd, windowPid);
+ if (pid == windowPid.getValue()) {
+ User32.INSTANCE.GetClassName(hwnd, windowClass, 100);
+ String windowClass_ = b2s(windowClass);
+ boolean b = windowClass_.equals("#32770");
+ if (windowClass_.equals("#32770")) {
+ dialogHandle.add(hwnd);
+ }
+ }
+ return true;
+ }},
+ Pointer.NULL);
+ switch (dialogHandle.size()) {
+ case 0: throw new RuntimeException("Dialog not found");
+ case 1: return dialogHandle.get(0);
+ default: throw new RuntimeException("Too many dialogs found");
+ }
+ }
+
+ public void SendKeysFileDialog(String text) {
+ if (MyUser32.INSTANCE == null) {
+ return;
+ }
+ HWND dialogHandle = FindOperaOpenDialog(operaPID);
+ HWND editHandle = null;
+ ArrayList<HWND> comboBoxes = GetChildWindowsByClass(dialogHandle, "ComboBox");
+ for (int i=0; i < comboBoxes.size(); i++) {
+ ArrayList<HWND> edits = GetChildWindowsByClass(comboBoxes.get(i), "Edit");
+ if (edits.size() > 0) {
+ if (edits.size() == 1) {
+ editHandle = edits.get(0);
+ break;
+ } else {
+ throw new RuntimeException("Too many edits found");
+ }
+ }
+ }
+ if (editHandle == null) {
+ throw new RuntimeException("Failed to find input element");
+ }
+ char[] c_text = (text + "\0").toCharArray();
+ MyUser32.INSTANCE.SendMessageW(editHandle, MyUser32.WM_SETTEXT, 0, c_text);
+
+ HWND openButton = MyUser32.INSTANCE.GetDlgItem(dialogHandle, 1);
+ if (openButton == null) {
+ throw new RuntimeException("Open button not found");
+ }
+ if (MyUser32.INSTANCE.SendMessageW(openButton, MyUser32.WM_LBUTTONDOWN, 0, null) +
+ MyUser32.INSTANCE.SendMessageW(openButton, MyUser32.WM_LBUTTONUP, 0, null) != 0) {
+ throw new RuntimeException("Couldn't click open button");
+ }
+ }
+
+ private ArrayList<HWND> GetChildWindowsByClass(HWND parent, final String className) {
+ final ArrayList<HWND> handles = new ArrayList<HWND>();
+ User32.INSTANCE.EnumChildWindows(parent, new User32.WNDENUMPROC() {
+ @Override
+ public boolean callback(HWND hwnd, Pointer pntr) {
+ char[] windowClass = new char[100];
+ User32.INSTANCE.GetClassName(hwnd, windowClass, 100);
+ if (b2s(windowClass).equals(className)) {
+ handles.add(hwnd);
+ }
+ return true;
+ }},
+ Pointer.NULL);
+ return handles;
+ }
+}