Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

fix sendKeys function for file-upload inputs #66

Closed
wants to merge 5 commits into from

2 participants

Sergej Orlov Andreas Tolfsen
Sergej Orlov

Current implementation of sendKeys for file inputs simply clicks on element, which causes system dialog to appear, but does not set text and close dialog.

Proposed fix uses jna library to send string to edit-element of open-file dialog and click OK button, similiar to IEDriver implementation.
Works on windows only, checked in WinXP and Win7.

Proposed by Mail.Ru Group

Andreas Tolfsen

Thanks for your patch!

However, I'm reluctant to integrate this because it's using the same approach as the IEDriver. All the communication with Opera should happen over the Scope protocol and this patch breaks fundamentally with that pattern.

I'm tempted to include it as a temporary solution to the problem of file input fields, but seeing as it only works on one OS and pulls in a lot of unnecessary dependencies I'm not sure we could do that.

I'm going to work on an Opera-side fix to this, though. Although it might take a little while. I encourage you to continue using this patch to OperaDriver in the meantime though, it seems to work good on Windows.

wladich added some commits
Sergej Orlov wladich Merge tag 'v0.11' of https://github.com/operasoftware/operadriver int…
…o file_input_sendkeys

[maven-release-plugin]  copy for tag v0.11

Conflicts:
	src/com/opera/core/systems/OperaWebElement.java
dd85782
Sergej Orlov wladich removed debug logging f1cdb81
Sergej Orlov wladich changed version 5d57392
Andreas Tolfsen

We are working on a patch to Scope which fixes this. I expect that to make it into live Operas post Opera 12.

Andreas Tolfsen andreastt closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Feb 22, 2012
Commits on Feb 27, 2012
Commits on Mar 16, 2012
  1. Sergej Orlov

    Merge tag 'v0.11' of https://github.com/operasoftware/operadriver int…

    wladich authored
    …o file_input_sendkeys
    
    [maven-release-plugin]  copy for tag v0.11
    
    Conflicts:
    	src/com/opera/core/systems/OperaWebElement.java
  2. Sergej Orlov

    removed debug logging

    wladich authored
  3. Sergej Orlov

    changed version

    wladich authored
This page is out of date. Refresh to see the latest.
BIN  lib/jna.jar
View
Binary file not shown
BIN  lib/platform.jar
View
Binary file not shown
2  maven/pom.xml
View
@@ -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>
100 src/com/opera/core/systems/OperaWebElement.java
View
@@ -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() {
}
}
-}
+}
114 src/com/opera/core/systems/util/FileSendKeys.java
View
@@ -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;
+ }
+}
Something went wrong with that request. Please try again.