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

Extend the list of default places to check for the Firefox executable #1045

Open
1kastner opened this issue Nov 4, 2017 · 21 comments
Open

Comments

@1kastner
Copy link

1kastner commented Nov 4, 2017

It seems like the geckodriver does not check all usual places for Firefox.

I tried to run the sample code as provided in this sample. However, I had to provide the path to the Firefox executable because it could not be found by itself. My working solution looks like this:

from selenium import webdriver
from selenium.webdriver.firefox.options import Options

opts = Options()
opts.binary = r"C:\Users\MY_USER_NAME\AppData\Local\Mozilla Firefox\firefox.exe"
driver = webdriver.Firefox(firefox_options=opts)

So my suggestion is to add %APPDATA%\..\Local\Mozilla Firefox\firefox.exe to the list of default places to check for Firefox. That seems at least to be the issue for me without any debugging steps and looking at it from outside. More details at the (wrongly) filed issue at selenium first and second.

@andreastt
Copy link
Contributor

andreastt commented Nov 5, 2017

As you can survey from https://github.com/jgraham/rust_mozrunner source code, we look at the Windows Registry to find out where Firefox is installed. Can you point out where that lookup is failing?

@1kastner
Copy link
Author

1kastner commented Nov 5, 2017

As I installed Mozilla Firefox only for my own user (that's why it is in %APPDATA%), the registry entries are in HKEY_CURRENT_USER but as far as I understand the code it checks only HKEY_LOCAL_MACHINE.

@andreastt
Copy link
Contributor

Thanks, that’s very useful information. We shall have to patch mozrunner.

@whimboo
Copy link
Collaborator

whimboo commented Nov 7, 2017

I filed jgraham/rust_mozrunner#15 for mozrunner given that this hasn't been done yet.

@florentbr
Copy link

In some cases with Windows, the driver doesn't return the default path for Firefox.

The current implementation reads these registry keys and returns the first match:

"HKEY_LOCAL_MACHINE\SOFTWARE\mozilla.org\Mozilla"  "CurrentVersion"
"HKEY_LOCAL_MACHINE\SOFTWARE\Mozilla\*" "GeckoVer"
"HKEY_LOCAL_MACHINE\SOFTWARE\Mozilla\*\bin" "PathToExe"

"HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\mozilla.org\Mozilla" "CurrentVersion"
"HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Mozilla\*"  "GeckoVer"
"HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Mozilla\*\bin" "PathToExe"

The driver should instead read these registry keys to get the path for Firefox:

HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\firefox.exe
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\firefox.exe
    (Default)    REG_SZ    C:\Program Files (x86)\FirefoxESR\firefox.exe
    Path    REG_SZ    C:\Program Files (x86)\FirefoxESR

It would be less complex and more reliable than the current implementation and since App Paths is a shared key, there's no need to check the 32/64bit redirection (\SOFTWARE\Wow6432Node).

Relevant code from the installer that writes the registry key:

https://dxr.mozilla.org/mozilla-central/source/browser/installer/windows/nsis/installer.nsi#440

@andreastt
Copy link
Contributor

It seems jgraham/rust_mozrunner#15 was never migrated over to Bugzilla, so I’m sorry that we lost track of this. This issue recently cropped up again for another user in #1168, so it’s time we fix this.

I’ve re-filed this as https://bugzilla.mozilla.org/show_bug.cgi?id=1574446.

@1kastner
Copy link
Author

Thanks for picking this up!

@andreastt
Copy link
Contributor

@1kastner @florentbr I think I have a patch that fixes this problem, but I’d like to ask your help to verify the fix.

How you could help:

  1. First ensure that you only have a 32-bit Firefox version (x86) installed on your system, either in your user directory or system-wide
  2. Download https://queue.taskcluster.net/v1/task/CK3y9DmrQquVGZVzuMQ6fA/runs/0/artifacts/public/build/target.common.tests.tar.gz (this is a 64-bit geckodriver)
  3. Extract bin/geckodriver.exe from the package
  4. Run a WebDriver script that does not specify a binary capability in moz:firefoxOptions

The expected outcome is for geckodriver to find the default location of the 32-bit Firefox on your system, despite running a 64-bit geckodriver.

Could you help out to verify it fixes this bug?

@1kastner
Copy link
Author

@andreastt Since the creation of the issue in 2017 some aspects of my machine have changed, e.g. my firefox installation has moved etc. I hope the below information can still be of help for you.

In [1]: from selenium import webdriver
   ...: driver = webdriver.Firefox(executable_path=r"<<path-to-the-specific-gecko-driver>>")
---------------------------------------------------------------------------
SessionNotCreatedException                Traceback (most recent call last)
<ipython-input-1-fe5761cf3a84> in <module>
      1 from selenium import webdriver
----> 2 driver = webdriver.Firefox(executable_path=r"xxxxxxxxxxxx\geckodriver.exe")

YYYYY\anaconda\lib\site-packages\selenium\webdriver\firefox\webdriver.py in __init__(self, firefox_profile, firefox_binary, timeout, capabilities, proxy, executable_path, options, service_log_path, firefox_options, service_args, desired_capabilities, log_path, keep_alive)
    172                 command_executor=executor,
    173                 desired_capabilities=capabilities,
--> 174                 keep_alive=True)
    175
    176         # Selenium remote

YYYYY\anaconda\lib\site-packages\selenium\webdriver\remote\webdriver.py in __init__(self, command_executor, desired_capabilities, browser_profile, proxy, keep_alive, file_detector, options)
    155             warnings.warn("Please use FirefoxOptions to set browser profile",
    156                           DeprecationWarning, stacklevel=2)
--> 157         self.start_session(capabilities, browser_profile)
    158         self._switch_to = SwitchTo(self)
    159         self._mobile = Mobile(self)

YYYYY\anaconda\lib\site-packages\selenium\webdriver\remote\webdriver.py in start_session(self, capabilities, browser_profile)
    250         parameters = {"capabilities": w3c_caps,
    251                       "desiredCapabilities": capabilities}
--> 252         response = self.execute(Command.NEW_SESSION, parameters)
    253         if 'sessionId' not in response:
    254             response = response['value']

YYYYY\anaconda\lib\site-packages\selenium\webdriver\remote\webdriver.py in execute(self, driver_command, params)
    319         response = self.command_executor.execute(driver_command, params)
    320         if response:
--> 321             self.error_handler.check_response(response)
    322             response['value'] = self._unwrap_value(
    323                 response.get('value', None))

YYYYY\anaconda\lib\site-packages\selenium\webdriver\remote\errorhandler.py in check_response(self, response)
    240                 alert_text = value['alert'].get('text')
    241             raise exception_class(message, screen, stacktrace, alert_text)
--> 242         raise exception_class(message, screen, stacktrace)
    243
    244     def _value_or_default(self, obj, key, default):

SessionNotCreatedException: Message: Unable to find a matching set of capabilities

@kaleidoskopio
Copy link

Would it be possible to do a new Quickstart for the people who don't understand properly the changes applied since the change to firefox? Thanks a lot in advance. I'm totally lost with these issues and I can't get my instapy to work : O

@1kastner
Copy link
Author

1kastner commented Sep 6, 2019

@kaleidoskopio can you be more precise about your question? What changes of firefox you refer to? What is your exact situation?

@whimboo
Copy link
Collaborator

whimboo commented Sep 9, 2019

@1kastner @florentbr I think I have a patch that fixes this problem, but I’d like to ask your help to verify the fix.

How you could help:

1. First ensure that you only have a 32-bit Firefox version (x86) installed on your system, either in your user directory or system-wide

2. Download https://queue.taskcluster.net/v1/task/CK3y9DmrQquVGZVzuMQ6fA/runs/0/artifacts/public/build/target.common.tests.tar.gz (this is a 64-bit geckodriver)

Note that this isn't the 64bit version but 32bit version. Generally it's useful to better point to the Treeherder changeset, and filtering for the expected jobs. Given that we now also have the geckodriver toolchain task, I would propose that the following binaries should be used for testing, which are also signed!

https://treeherder.mozilla.org/#/jobs?repo=try&revision=ea03b3a88daadec376d2555c368f935e2dcd3f7c&searchStr=geckodriver%2Csign

Just click the green s job for the binary to test, and select the geckodriver.zip file in the lower pane. This archive only contains the geckodriver executable.

Note that this referenced build will expire on Sep 19th, and would need to be rebuilt if no verification can be done until that given day.

@1kastner
Copy link
Author

1kastner commented Sep 9, 2019

It seems like that the prepared geckodriver is not the whole answer for me:

>>> from selenium import webdriver
>>> driver = webdriver.Firefox(executable_path=r"path-to-the-prepared\geckodriver.exe")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "E:\anaconda\lib\site-packages\selenium\webdriver\firefox\webdriver.py", line 174, in __init__
    keep_alive=True)
  File "E:\anaconda\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 157, in __init__
    self.start_session(capabilities, browser_profile)
  File "E:\anaconda\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 252, in start_session
    response = self.execute(Command.NEW_SESSION, parameters)
  File "E:\anaconda\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 321, in execute
    self.error_handler.check_response(response)
  File "E:\anaconda\lib\site-packages\selenium\webdriver\remote\errorhandler.py", line 242, in check_response
    raise exception_class(message, screen, stacktrace)
selenium.common.exceptions.SessionNotCreatedException: Message: Unable to find a matching set of capabilities

But this one worked:

In [2]: from selenium import webdriver
   ...: from selenium.webdriver.firefox.options import Options
   ...:
   ...: opts = Options()
   ...: opts.binary = r"C:\Program Files (x86)\Mozilla Firefox\firefox.exe"
   ...: driver = webdriver.Firefox(firefox_options=opts, executable_path=r"path-to-the-prepared\geckodriver.exe"
   ...: )
E:\anaconda\Scripts\ipython:6: DeprecationWarning: use options instead of firefox_options

In case you wondered about whether anything else could have gone wrong, it is definitely not the geckodriver, see

In [1]: from selenium import webdriver
   ...: from selenium.webdriver.firefox.options import Options
   ...:
   ...: opts = Options()
   ...: opts.binary = r"C:\Program Files (x86)\Mozilla Firefox\firefox.exe"
   ...: driver = webdriver.Firefox(firefox_options=opts)
E:\anaconda\Scripts\ipython:6: DeprecationWarning: use options instead of firefox_options
---------------------------------------------------------------------------
FileNotFoundError                         Traceback (most recent call last)
E:\anaconda\lib\site-packages\selenium\webdriver\common\service.py in start(self)
     75                                             stderr=self.log_file,
---> 76                                             stdin=PIPE)
     77         except TypeError:

E:\anaconda\lib\subprocess.py in __init__(self, args, bufsize, executable, stdin, stdout, stderr, preexec_fn, close_fds, shell, cwd, env, universal_newlines, startupinfo, creationflags, restore_signals, start_new_session, pass_fds, encoding, errors)
    708                                 errread, errwrite,
--> 709                                 restore_signals, start_new_session)
    710         except:

E:\anaconda\lib\subprocess.py in _execute_child(self, args, executable, preexec_fn, close_fds, pass_fds, cwd, env, startupinfo, creationflags, shell, p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite, unused_restore_signals, unused_start_new_session)
    996                                          os.fspath(cwd) if cwd is not None else None,
--> 997                                          startupinfo)
    998             finally:

FileNotFoundError: [WinError 2] Das System kann die angegebene Datei nicht finden

During handling of the above exception, another exception occurred:

WebDriverException                        Traceback (most recent call last)
<ipython-input-1-34aa8ca74142> in <module>
      4 opts = Options()
      5 opts.binary = r"C:\Program Files (x86)\Mozilla Firefox\firefox.exe"
----> 6 driver = webdriver.Firefox(firefox_options=opts)

E:\anaconda\lib\site-packages\selenium\webdriver\firefox\webdriver.py in __init__(self, firefox_profile, firefox_binary, timeout, capabilities, proxy, executable_path, options, service_log_path, firefox_options, service_args, desired_capabilities, log_path, keep_alive)
    162                 service_args=service_args,
    163                 log_path=service_log_path)
--> 164             self.service.start()
    165
    166             capabilities.update(options.to_capabilities())

E:\anaconda\lib\site-packages\selenium\webdriver\common\service.py in start(self)
     81                 raise WebDriverException(
     82                     "'%s' executable needs to be in PATH. %s" % (
---> 83                         os.path.basename(self.path), self.start_error_message)
     84                 )
     85             elif err.errno == errno.EACCES:

WebDriverException: Message: 'geckodriver' executable needs to be in PATH.

When I scanned my registry, I found the following:

  • Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Mozilla\Firefox\TaskBarIDs with the entry C:\Program Files (x86)\Mozilla Firefox,
  • Computer\HKEY_CURRENT_USER\Software\Mozilla\Firefox\Launcher with four different entries pointing at the executable
  • Computer\HKEY_CURRENT_USER\Software\Mozilla\Mozilla Firefox\69.0 (x64 en-US)\Main with the keys Install Directory and PathToExe
  • Computer\HKEY_CURRENT_USER\Software\Mozilla\Mozilla Firefox 69.0\bin with the key PathToExe
  • and many more...

When I opened the issue, the executable was in %APPDATA%..\Local but after some updates and changes this is not the case anymore.

@1kastner 1kastner changed the title Add %APPDATA%\..\Local\Mozilla Firefox\firefox.exe to the list of default places to check for Firefox Extend the list of default places to check for the Firefox executable Sep 9, 2019
@1kastner
Copy link
Author

1kastner commented Sep 9, 2019

@whimboo PS: How can I activate logging so that I can see which registry keys are looked for in which order?

@whimboo
Copy link
Collaborator

whimboo commented Sep 16, 2024

@1kastner sorry for the extreme delay here. Looks like this issue got under the wire.

In the last years a lot has changed. So is it still happening to you? If yes please feel free to reopen the issue.

@whimboo whimboo closed this as not planned Won't fix, can't repro, duplicate, stale Sep 16, 2024
@whimboo
Copy link
Collaborator

whimboo commented Sep 16, 2024

Oh wait. There is a referenced bug above which hasn't been fixed: https://bugzilla.mozilla.org/show_bug.cgi?id=1574446. So it's probably still a problem.

@whimboo whimboo reopened this Sep 16, 2024
@1kastner
Copy link
Author

Thank you for monitoring this.

@florentbr
Copy link

@whimboo, I had a quick look at the patch and I think it's not working because it's extracting the path from a subkey when it should be the default value (empty string). This should work:

-    const APP_PATHS: &str = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths";
+    const APP_PATHS: &str = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\firefox.exe";

        for hkey in [HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE].iter() {
            let hklm = RegKey::predef(*hkey);
            let app_paths = hklm.open_subkey_with_flags(APP_PATHS, KEY_READ)?;
-           let maybe_exe: Result<String, _> = app_paths.get_value("firefox.exe");
+           let maybe_exe: Result<String, _> = app_paths.get_value("");
            if let Ok(exe) = maybe_exe {

@whimboo
Copy link
Collaborator

whimboo commented Oct 1, 2024

@florentbr thanks a lot for following up. Due to a recent bug in Firefox not being able to find the default installation we stumbled over this issue again. Specifically supporting checks under HKCU I've filed https://bugzilla.mozilla.org/show_bug.cgi?id=1921933. Would you be interested to contribute and offer a patch? I would be glad to mentor in case of issues working with our mono repository.

@florentbr
Copy link

@florentbr thanks a lot for following up. Due to a recent bug in Firefox not being able to find the default installation we stumbled over this issue again. Specifically supporting checks under HKCU I've filed https://bugzilla.mozilla.org/show_bug.cgi?id=1921933. Would you be interested to contribute and offer a patch? I would be glad to mentor in case of issues working with our mono repository.

I would have been interested in 2017 when this issue was raised. I no longer use Windows these days.

@whimboo
Copy link
Collaborator

whimboo commented Oct 1, 2024

Ok, no worries then. We should hopefully get to it eventually. We will then take your hint into account. Thanks again!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants