Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's the difference between `driver.execute_script("...")` and `driver.get("javascript: ..."` with geckodriver/Firefox?

I think, this question concerns the internal workings of Selenium. In another post Referer missing in HTTP header of Selenium request it becomes apparent that there is a difference between running

driver.execute_script("window.location.href = '{}';".format(url))

and

driver.get("javascript: window.location.href = '{}'".format(url))

The latter command will send Referer header with the request, the former will not.

Doesn't matter at this point if this is desired behavior or a bug and Referer should be sent by both commands. Also, window.location.href = ... is only an example.

Yet, obviously, there must be a difference between running JavaScript with command driver.execute_script("...") and driver.get("javascript: ..." if they don't produce the same result. So the question is more about the fact that both commands don't invoke the same Selenium code internally.

What is the difference between both commands?

like image 709
finefoot Avatar asked Jun 06 '19 09:06

finefoot


2 Answers

The answer to your question depends on the browser your driver is running. Selenium itself does not implement these functionalities - it merely invokes the underlying driver's API.

Take a look at the source of WebDriver.execute_script and WebDriver.get - they both just call self.execute, which performs a request to the webdriver.

Chrome, for example, does not support 'javascript:' urls with WebDriver.get since 2013, as can bee seen in chromium's webdriver implementation.

The actual difference between directly running a JS script and navigating to a 'javascript URL' is embedded deep in each browser's implementation, and might be not very straightforward. A possible reason for the difference you mentioned could be an implementation detail - maybe the browser (that was used when the results you mentioned were produced) only sends a Referer header when it is in the context of a high level navigation command (driver.get), and therefore did not include one on a plain javascript-triggered navigation.

like image 86
kmaork Avatar answered Nov 14 '22 16:11

kmaork


TL;DR: I was curious about this and was starting an answer. Then went out of town.

I'm not trying to poach points or anything from @Ni. As he points out, get and execute_script call self.execute, which--in turn--calls a method from the Command class. For example, Command.GET or Command.EXECUTE_SCRIPT. And that's where the trail went cold for me...


the source code

https://github.com/SeleniumHQ/selenium/blob/master/py/selenium/webdriver/remote/webdriver.py

def get(self, url):
    """
    Loads a web page in the current browser session.
    """
    self.execute(Command.GET, {'url': url})

and

def execute_script(self, script, *args):
    """
    Synchronously Executes JavaScript in the current window/frame.

    :Args:
     - script: The JavaScript to execute.
     - \*args: Any applicable arguments for your JavaScript.

    :Usage:
        driver.execute_script('return document.title;')
    """
    converted_args = list(args)
    command = None
    if self.w3c:
        command = Command.W3C_EXECUTE_SCRIPT
    else:
        command = Command.EXECUTE_SCRIPT

    return self.execute(command, {
        'script': script,
        'args': converted_args})['value']

which points to

https://github.com/SeleniumHQ/selenium/blob/master/py/selenium/webdriver/remote/command.py

class Command(object):
    """
    Defines constants for the standard WebDriver commands.
    While these constants have no meaning in and of themselves, they are
    used to marshal commands through a service that implements WebDriver's
    remote wire protocol:
        https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol
    """

and

https://github.com/SeleniumHQ/selenium/blob/master/py/selenium/webdriver/remote/remote_connection.py#L142 shows a private method called self._commands, which is a dictionary containing commands that mirror the syntax seen in ..remote/webdriver.py

For example: Command.GET: ('POST', '/session/$sessionId/url') vs. self.execute(Command.GET, {'url': url})

The endpoints in self._commands correspond to the https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol#command-reference, so this is the service "used to marshal commands" (?) or part of it...

(ruby equiv: https://github.com/SeleniumHQ/selenium/blob/master/rb/lib/selenium/webdriver/remote/commands.rb)

like image 1
orde Avatar answered Nov 14 '22 16:11

orde