Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

synthetic events do not generate focus and blur unless FF is the topmost window #825

Closed
lddubeau opened this issue Jul 23, 2015 · 10 comments
Assignees

Comments

@lddubeau
Copy link

Versions

Selenium version: 2.45.0, 2.46.1
Browser: FF 34
OS: Debian Testing up to date as of 2015-07-23

What steps will reproduce the problem?

To get the whole script to run you should run it on 2.45.0. See notes below regarding 2.46.0.

  1. This code reproduces the problem 100% of the time here:

    from selenium import webdriver
    from selenium.webdriver.common.action_chains import ActionChains
    from selenium.webdriver.firefox.webdriver import FirefoxBinary
    from selenium.common.exceptions import ElementNotVisibleException
    from selenium.common.exceptions import InvalidElementStateException
    
    import time
    import subprocess
    
    def pause():
    time.sleep(1)
    
    def makedriver(native):
    desired_capabilities = {
        "nativeEvents": native
    }
    driver = webdriver.Firefox(
        firefox_binary=FirefoxBinary(
            "/home/ldd/src/firefox-34.0.5/firefox"),
        capabilities=desired_capabilities)
    
    print driver.desired_capabilities["platform"]
    print driver.name
    print driver.desired_capabilities["version"]
    return driver
    
    def perform(native):
    driver = makedriver(native)
    
    # Make this False to easily turn off the creation of the new window.
    if True:
        #
        # This will open a new window which will mess up the synthetic
        # event generation.
        #
        with open("/dev/null") as null:
            ff = subprocess.Popen(["xterm", "-e", "/usr/bin/top"],
                                  stdout=null, stderr=null)
    else:
        ff = None
    
    print "NATIVE:", driver.desired_capabilities["nativeEvents"]
    
    driver.get("http://jsbin.com/picuxo")
    
    field = driver.find_element_by_id("input")
    
    ActionChains(driver) \
        .move_to_element(field) \
        .click() \
        .send_keys("foo") \
        .perform()
    
    #
    # The pauses should satisfy observers that the unexpected results
    # we are getting with native events turned off are not due to some
    # asynchronous browser-side process not having had time to run.
    #
    # I'm using the brute approach of using `time.sleep` so as to not
    # get bogged down in polling logic.
    #
    pause()
    
    print "START:", driver.execute_script("return arguments[0].value", field)
    
    fill = driver.find_element_by_id("fill")
    try:
        ActionChains(driver) \
            .click(fill) \
            .perform()
    except ElementNotVisibleException:
        # We should not see this.
        print "*** FILL ELEMENT NOT VISIBLE!"
    
    pause()
    
    # The field should contain `foofilled` because the `Fill` button
    # added text to it.
    print "FILLED:", driver.execute_script("return arguments[0].value", field)
    
    button = driver.find_element_by_id("submit")
    ActionChains(driver) \
        .click(button) \
        .perform()
    
    pause()
    
    modal2 = driver.find_element_by_id("modal2")
    # It should not be visible because modal1 is covering the button.
    print "MODAL2 VISIBLE?", modal2.is_displayed()
    
    ActionChains(driver) \
        .move_to_element(field) \
        .click() \
        .send_keys("bar") \
        .perform()
    
    dismiss = driver.find_element_by_css_selector("#modal1 button")
    try:
        ActionChains(driver) \
            .click(dismiss) \
            .perform()
    except ElementNotVisibleException:
        # We should not see this.
        print "*** MODAL1 DISMISS ELEMENT NOT VISIBLE!"
    
    ActionChains(driver) \
        .click(button) \
        .perform()
    
    pause()
    
    ActionChains(driver) \
        .move_to_element(field) \
        .click() \
        .send_keys("baz") \
        .perform()
    
    pause()
    
    # The final value should be `foofilled` because the `send_keys`
    # that were called after the `Fill` button was clicked should have
    # had no effect.
    print "FINAL:", driver.execute_script("return arguments[0].value", field)
    print
    
    driver.quit()
    
    if ff:
        ff.terminate()
    
    for native in (False, True):
    try:
        perform(native)
    except InvalidElementStateException:
        # This will happen when running with >= 2.46.0
        print "*** Aborted due to the unavailability of native events!"
    
    
  2. Save the script as issue.py. Edit the script so that the path given for the Firefox binary for which native events can be produced is set to a path that works on your system, and run:

    $ xvfb-run python ./issue.py

xvfb-run is used to isolate the run from other applications running. If you just run it on your usual desktop, you may not get the results above due to window manager interference or some other factor.

What is the expected output?

Linux
firefox
34.0.5
NATIVE: False
START: foo
FILLED: foofilled
MODAL2 VISIBLE? False
FINAL: foofilled

Linux
firefox
34.0.5
NATIVE: True
START: foo
FILLED: foofilled
MODAL2 VISIBLE? False
FINAL: foofilled

Synthetic events should produce the same browser behavior as native events.

What do you see instead?

Linux
firefox
34.0.5
NATIVE: False
START: foo
*** FILL ELEMENT NOT VISIBLE!
FILLED: foo
MODAL2 VISIBLE? True
*** MODAL1 DISMISS ELEMENT NOT VISIBLE!
FINAL: foobarbaz

Linux
firefox
34.0.5
NATIVE: True
START: foo
FILLED: foofilled
MODAL2 VISIBLE? False
FINAL: foofilled

The main problem here is that focus and blur events are not generated, so elements that should become visible on the screen do not become visible.

Observations

It is not possible to run the whole script with Selenium 2.46.0. The script is designed to show how the behavior of native events and synthetic events differ, with native events providing exactly what a developer would expect. Because 2.46.0 does not support native events on FF 34, the attempt at running with native events fails. However, it is possible to run the script in 2.46.0 and see the incorrect behavior provided by synthetic events, which is the same as in 2.45.0.

Running the script with xvfb-run and launching an xterm instance so that Firefox is not the topmost window is a means to get 100% reproducibility. On my own desktop here (Gnome) I can readily reproduce the problem if I just run python ./issue.py, and even if Firefox looks like it is the topmost window. However, I found that if I start playing with the mouse while the test runs, reproducibility is not guaranteed.

Running the script with Chrome 44 (and chromedriver 2.16) produces the expected output, except for the fact that we get NATIVE: True in both runs given that it is not possible to turn off native events in Chrome. I'm not equipped to try to mimic what I'm doing in X11 in a Windows or Mac OS environment but I'd expect native events there too to work perfectly like they do in Chrome 44.

I'm reporting the bug as requested here.

@barancev barancev self-assigned this Sep 18, 2015
@barancev
Copy link
Member

Fixed by commit 0eec81d

@lddubeau
Copy link
Author

I've cloned the latest version of Selenium available on Github (default branch: master) and found both #862 and #813 to be resolved but if I run the code in the initial issue report here, I still get the incorrect behavior. I've also eyeballed the patch and did not notice anything having to do with focus events.

So from my perspective, the commit you've referenced in your comment does fix #862 and #813 but it does not fix this issue.

@jleyba
Copy link
Contributor

jleyba commented Sep 21, 2015

@barancev Going back to FF2 Firefox has had logic where it won't process focus or blur events when the app is not active. For linux this can be "fixed" with FirefoxProfile#setAlwaysLoadNoFocusLib

AFAIK there is no work around for Windows.

@barancev
Copy link
Member

I'm not sure about blur and focus events. I just ran the scenario from this issue (translated to Java) and got the result that is described as "expected" above. I'll retest tomorrow.

@barancev barancev reopened this Sep 21, 2015
@lddubeau
Copy link
Author

I've run into this problem again and narrowed it down to a much simpler case:

import subprocess

import selenium
from selenium import webdriver
from selenium.webdriver.firefox.webdriver import FirefoxBinary
from selenium.webdriver.common.action_chains import ActionChains

def perform(driver):
    # Make this False to easily turn off the creation of the new window.
    if True:
        #
        # This will open a new window which will mess up the synthetic
        # event generation.
        #
        with open("/dev/null") as null:
            ff = subprocess.Popen(["xterm", "-e", "/usr/bin/top"],
                                  stdout=null, stderr=null)
    else:
        ff = None

    driver.get("http://example.com")
    text = driver.execute_script("""
    var body = document.body;
    body.innerHTML += \
      "<p id='text' contenteditable>Foobar</p>";
    var text = document.getElementById("text");
    window.log = [];
    text.addEventListener("focus", function (ev) {
        log.push("text focus");
    });

    return text;
    """)

    print "selenium:", selenium.__version__
    print "browser:", driver.desired_capabilities["platform"], driver.name, \
        driver.desired_capabilities["version"]
    print "native events:", driver.desired_capabilities["nativeEvents"]

    print driver.execute_script("""
    var el = document.activeElement;
    return el.tagName + " " + el.id;
    """)

    print driver.execute_script("return log")

    ActionChains(driver) \
        .click(text) \
        .perform()

    print driver.execute_script("""
    var el = document.activeElement;
    return el.tagName + " " + el.id;
    """)

    print driver.execute_script("return log")

    driver.quit()

    if ff:
        ff.terminate()

driver = webdriver.Firefox(
    firefox_binary=FirefoxBinary("/home/ldd/src/firefox-31/firefox"),
    capabilities={"nativeEvents": True})
perform(driver)

driver = webdriver.Firefox(
    firefox_binary=FirefoxBinary("/home/ldd/src/firefox-31/firefox"),
    capabilities={"nativeEvents": False})
perform(driver)

driver = webdriver.Firefox(
    firefox_binary=FirefoxBinary("/home/ldd/src/firefox-38/firefox"))
perform(driver)

driver = webdriver.Chrome()
perform(driver)

Run it with xvfb-run python ./issue.py

Reminder: using xvfb-run is a convenient way to isolate FF from our desktop. The issue happens only when FF is not the topmost window.

Expected output

Each run of perform should show the following results:

BODY 
[]
P text
[u'text focus']

It shows that document.activeElement went from the body element to the p element with id text. The two arrays are a dump of the log global which is empty before the click and contains "text focus" after the click, indicating that the focus event was processed.

I get the expected output if I run the test with Selenium 2.45.0 and I edit the code to turn off the launch of xterm:

selenium: 2.45.0
browser: Linux firefox 31.8.0
native events: True
BODY 
[]
P text
[u'text focus']
selenium: 2.45.0
browser: Linux firefox 31.8.0
native events: False
BODY 
[]
P text
[u'text focus']
selenium: 2.45.0
browser: Linux firefox 38.0
native events: False
BODY 
[]
P text
[u'text focus']
selenium: 2.45.0
browser: Linux chrome 47.0.2526.106
native events: True
BODY 
[]
P text
[u'text focus']

Actual output

When using synthetic events and FF is not the topmost window, then the focus event is not delivered.

For instance with Selenium 2.45.0 with FF not the topmost window (xterm is run to block it from view):

selenium: 2.45.0
browser: Linux firefox 31.8.0
native events: True
BODY 
[]
P text
[u'text focus']
selenium: 2.45.0
browser: Linux firefox 31.8.0
native events: False
BODY 
[]
P text
[]
selenium: 2.45.0
browser: Linux firefox 38.0
native events: False
BODY 
[]
P text
[]
selenium: 2.45.0
browser: Linux chrome 47.0.2526.106
native events: True
BODY 
[]
P text
[u'text focus']

Or with Selenium 2.48, it is even worse. Even with FF 31.8 we get the bad results because somehow 2.48 is unable to turn on native events on FF 31.8:

selenium: 2.48.0
browser: Linux firefox 31.8.0
native events: False
BODY 
[]
P text
[]
selenium: 2.48.0
browser: Linux firefox 31.8.0
native events: False
BODY 
[]
P text
[]
selenium: 2.48.0
browser: Linux firefox 38.0
native events: False
BODY 
[]
P text
[]
selenium: 2.48.0
browser: Linux chrome 47.0.2526.106
native events: True
BODY 
[]
P text
[u'text focus']

@lddubeau
Copy link
Author

I checked out whether setAlwaysLoadNoFocusLib was something that would affect the results I get. I looked for an equivalent function for the Python distribution of Selenium but found nothing of the sort. In the Python implementation it seems that there is a library that is always made available to FF, irrespective of any flags. To check this, I started a Selenium session and examined the process through /proc.

$ xargs -n 1 -0 echo < /proc/25854/environ | grep LD_
LD_LIBRARY_PATH=/tmp/tmp5PJ0dS/x86:/tmp/tmp5PJ0dS/amd64:
LD_PRELOAD=x_ignore_nofocus.so

$ ls -l /tmp/tmp5PJ0dS/{x86,amd64}
/tmp/tmp5PJ0dS/amd64:
total 44
-rwxr-xr-x 1 ldd ldd 41262 Jan 13 08:54 x_ignore_nofocus.so

/tmp/tmp5PJ0dS/x86:
total 32
-rwxr-xr-x 1 ldd ldd 30887 Jan 13 08:54 x_ignore_nofocus.so

And the library is actually mapped in the process' space:

$ ls -al /proc/25854/map_files/ | grep nofocus
lr-------- 1 ldd ldd 64 Jan 13 08:55 7f9ab142b000-7f9ab1433000 -> /tmp/tmp5PJ0dS/amd64/x_ignore_nofocus.so
lr-------- 1 ldd ldd 64 Jan 13 08:55 7f9ab1433000-7f9ab1632000 -> /tmp/tmp5PJ0dS/amd64/x_ignore_nofocus.so
lr-------- 1 ldd ldd 64 Jan 13 08:55 7f9ab1632000-7f9ab1633000 -> /tmp/tmp5PJ0dS/amd64/x_ignore_nofocus.so
lr-------- 1 ldd ldd 64 Jan 13 08:55 7f9ab1633000-7f9ab1634000 -> /tmp/tmp5PJ0dS/amd64/x_ignore_nofocus.so

Is there something additional that needs to be triggered to get the library activated in the executable?

@lddubeau
Copy link
Author

I can reproduce the issue in Java. It's been ages since I've worked with Java, so it is not impossible I'm doing funky stuff but here is the code:

import java.util.Properties;
import java.io.IOException;
import java.io.File;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.jar.Attributes;
import java.net.URISyntaxException;
import java.lang.Process;

import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.openqa.selenium.Capabilities;
import org.openqa.selenium.interactions.Actions;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.firefox.FirefoxBinary;

public class issue {
    private static void perform(RemoteWebDriver driver) throws IOException,
        URISyntaxException {

        // There does not appear to be an easier way to get Selenium's
        // version in Java.
        File jar_file = new File(
            WebElement.class.getProtectionDomain().getCodeSource()
            .getLocation().toURI().g‌​etPath().toString());
        JarFile jar = new JarFile(jar_file);
        Manifest manifest = jar.getManifest();
        Attributes attrs = manifest.getAttributes("Build-Info");
        String version = attrs.getValue("Selenium-Version");

        Process xterm = null;
        if (true) {
            xterm = Runtime.getRuntime().exec(new String[] {
                    "xterm", "-e", "/usr/bin/top"});
        }
        else {
            xterm = null;
        }

        driver.get("http://example.com");
        WebElement text = (WebElement) driver.executeScript(
            "var body = document.body;" +
            "body.innerHTML += \"<p id='text' contenteditable>Foobar</p>\";" +
            "var text = document.getElementById(\"text\");" +
            "window.log = [];" +
            "text.addEventListener(\"focus\", function (ev) {"+
              "log.push(\"text focus\");" +
            "});"+
            "return text;");

        Capabilities caps = driver.getCapabilities();
        System.out.println("selenium: " + version);
        System.out.println("browser: " +
                           caps.getPlatform().toString() + " " +
                           caps.getBrowserName() + " " +
                           caps.getVersion());
        System.out.println("native events: " +
                           caps.getCapability("nativeEvents").toString());

        System.out.println(driver.executeScript(
                               "var el = document.activeElement;" +
                               "return el.tagName + ' ' + el.id;"));

        System.out.println(driver.executeScript("return log"));

        new Actions(driver).click(text).perform();

        System.out.println(driver.executeScript(
                               "var el = document.activeElement;" +
                               "return el.tagName + ' ' + el.id;"));

        System.out.println(driver.executeScript("return log"));

        driver.quit();

        if (xterm != null)
            xterm.destroy();
    }


    public static void main(String[] args) throws Exception {
        DesiredCapabilities caps = new DesiredCapabilities();
        caps.setCapability("nativeEvents", true);
        RemoteWebDriver driver = new FirefoxDriver(
            new FirefoxBinary(new File("/home/ldd/src/firefox-31/firefox")),
            null, null, caps);
        perform(driver);

        driver = new FirefoxDriver(
            new FirefoxBinary(new File("/home/ldd/src/firefox-31/firefox")),
            null);
        perform(driver);

        driver = new FirefoxDriver(
            new FirefoxBinary(new File("/home/ldd/src/firefox-38/firefox")),
            null);
        perform(driver);

        driver = new ChromeDriver();
        perform(driver);

    }
}

Output with 2.45.0:

selenium: 2.45.0
browser: LINUX firefox 31.8.0
native events: true
BODY 
[]
P text
[text focus]
selenium: 2.45.0
browser: LINUX firefox 31.8.0
native events: false
BODY 
[]
P text
[]
selenium: 2.45.0
browser: LINUX firefox 38.0
native events: false
BODY 
[]
P text
[]
Starting ChromeDriver 2.20.353124 (035346203162d32c80f1dce587c8154a1efa0c3b) on port 26640
Only local connections are allowed.
[0.066][SEVERE]: After loading Root Certs, loaded==false: NSS error code: -8018
selenium: 2.45.0
browser: LINUX chrome 47.0.2526.106
native events: true
BODY 
[]
P text
[text focus]

Output with 2.48.2:

selenium: 2.48.2
browser: LINUX firefox 31.8.0
native events: false
BODY 
[]
P text
[]
selenium: 2.48.2
browser: LINUX firefox 31.8.0
native events: false
BODY 
[]
P text
[]
selenium: 2.48.2
browser: LINUX firefox 38.0
native events: false
BODY 
[]
P text
[]
Starting ChromeDriver 2.20.353124 (035346203162d32c80f1dce587c8154a1efa0c3b) on port 8037
Only local connections are allowed.
[0.563][SEVERE]: After loading Root Certs, loaded==false: NSS error code: -8018
selenium: 2.48.2
browser: LINUX chrome 47.0.2526.106
native events: true
BODY 
[]
P text
[text focus]

I've also tried with:

        FirefoxProfile profile = new FirefoxProfile();
        profile.setAlwaysLoadNoFocusLib(true);

and passed profile to the FirefoxDriver instances but it made no difference.

@lddubeau
Copy link
Author

Adding this before clicking on text in the Python code produces the desired behavior:

    ActionChains(driver) \
        .click(driver.find_element_by_tag_name("body")) \
        .perform()

@barancev
Copy link
Member

Works in Firefox ESR 52 + legacy Firefox driver, tested in Selenium 3.5.2, added regression tests by 30ebe6e

This issue is actual for geckodriver, follow mozilla/geckodriver#906

@barancev
Copy link
Member

Well, it may still fail in legacy Firefox driver (especially on Linux), but it's not going to be fixed there anyway.

Tom-Trumper pushed a commit to Tom-Trumper/selenium that referenced this issue Sep 11, 2017
@lock lock bot locked and limited conversation to collaborators Aug 17, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

3 participants