0

I'm trying to retrieve the value of navigator.plugins from a Selenium driven ChromeDriver initiated Browsing Context.

Using I'm able to retrieve navigator.userAgent and navigator.plugins as follows:

navigator_userAgent_plugins

But using Selenium's execute_script() method I'm able to extract the navigator.userAgent but navigator.plugins raises the following circular reference error:

  • Code Block:

    from selenium import webdriver 
    
    options = webdriver.ChromeOptions() 
    options.add_argument("start-maximized")
    driver = webdriver.Chrome(options=options, executable_path=r'C:\WebDrivers\chromedriver.exe')
    driver.get("https://www.google.com/")
    print("userAgent: "+driver.execute_script("return navigator.userAgent;"))
    print("plugins: "+driver.execute_script("return navigator.plugins;"))
    
  • Console Output:

    userAgent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36
    Traceback (most recent call last):
      File "C:\Users\Soma Bhattacharjee\Desktop\Debanjan\PyPrograms\navigator_properties.py", line 19, in <module>
        print("vendor: "+driver.execute_script("return navigator.plugins;"))
      File "C:\Python\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 636, in execute_script
        'args': converted_args})['value']
      File "C:\Python\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 321, in execute
        self.error_handler.check_response(response)
      File "C:\Python\lib\site-packages\selenium\webdriver\remote\errorhandler.py", line 242, in check_response
        raise exception_class(message, screen, stacktrace)
    selenium.common.exceptions.JavascriptException: Message: javascript error: circular reference
      (Session info: chrome=83.0.4103.116)
    

I've been through the following discussions on circular reference and I understand the concept. But I am not sure how should I address the issue here.

Can someone help me to retrieve the navigator.plugins please?

undetected Selenium
  • 183,867
  • 41
  • 278
  • 352

3 Answers3

3

There might be a serialization issue when you query a non-primitive data structure from a browser realm. By closely inspecting the hierarchy of a single plugin, we can see it has a recursive structure which is an issue for the serializer. enter image description here

If you need a list of plugins, try returning just a serialized, newline-separated string and then split it by a newline symbol in the Python realm.

For example:

plugins = driver.execute_script("return Array.from(navigator.plugins).map(({name}) => name).join('\n');").split('\n')
  • With `print("plugins->name: "+driver.execute_script("return Array.from(navigator.plugins).map(({name}) => name);"))` I'm seeing _TypeError: must be str, not list_ – undetected Selenium Jul 04 '20 at 20:51
  • Ok, then just return it as newline-separated string. For example: return Array.from(navigator.plugins).map(({name}) => name).join('\n'); Then just split it by \n in your Python code to get a list of stirngs. – Viacheslav Moskalenko Jul 04 '20 at 20:52
  • With `print("plugins->name: "+driver.execute_script("return Array.from(navigator.plugins).map(({name}) => name);")[0])` I get back _Chrome PDF Plugin_, _Chrome PDF Viewer_ and so on. – undetected Selenium Jul 04 '20 at 20:54
  • That is not what I suggested above. In your case you just return the name of a first available plugin. What I suggested to do is to return a newline-separated string from your call to execute_script and then slit it by "\n" in your Python code to get a list of stirngs. For example: plugins = driver.execute_script("return Array.from(navigator.plugins).map(({name}) => name).join('\n')").split('\n') print('plugins->name: ' + plugins) – Viacheslav Moskalenko Jul 04 '20 at 20:58
  • @ViacheslavMoskalenko All you need to add is `.join()` to the end and it will return a comma-delimited string of names, `return Array.from(navigator.plugins).map(({name}) => name).join()` – JeffC Jul 04 '20 at 21:00
  • @JeffC, The separator could be anything. A newline is just an example. If a plugin has "," as part of the name, then we shouldn't be relying on the default separator, which is a comma. – Viacheslav Moskalenko Jul 04 '20 at 21:01
  • Understood but you are taking an array of string, joining it, then splitting it. I don't understand the purpose. Your answer is returning an array, which is likely a problem. If you add just `.join()` it will return a string which will be fine. – JeffC Jul 04 '20 at 21:03
  • I'll adjust my answer because I wasn't aware execute_script can only return a string. If you take a look at my comments above, I've suggested gluing a list of strings (the available plugin names) to be later split in the Python realm. The author needs a list of available plugins which is why we have to split a string returned to us by execute_script. – Viacheslav Moskalenko Jul 04 '20 at 21:05
  • @DebanjanB, does that matches result you were initially envisioned? – Viacheslav Moskalenko Jul 04 '20 at 21:09
  • @ViacheslavMoskalenko Your initial answer took me to a point where I'm able to dig further. Definitely, that was helpful. I'm still trying to retrieve the _plugin_ values. – undetected Selenium Jul 04 '20 at 21:11
  • @ViacheslavMoskalenko The previous version of your answer was working. Can you update the line of code as per the previous version please? – undetected Selenium Jul 16 '20 at 18:31
  • @DebanjanB, could you, please, clarify? What isn't working at the moment? – Viacheslav Moskalenko Aug 04 '20 at 06:21
1

I'm assuming it has something to do with the fact that navigator.plugins returns a PluginArray.

The PluginArray page lists the methods and properties that are available and with that I wrote this code that returns the list of names. You can adapt it to whatever you need.

print("plugins: " + driver.execute_script("var list = [];for(var i = 0; i < navigator.plugins.length; i++) { list.push(navigator.plugins[i].name); }; return list.join();"))
JeffC
  • 22,180
  • 5
  • 32
  • 55
  • Definitely this answer helps to move one step ahead. Somehow, I need to get the _plugin_ values. – undetected Selenium Jul 04 '20 at 21:14
  • What plugin values are you looking for specfically? – JeffC Jul 04 '20 at 21:34
  • I was looking to extract the values from 1) Chrome PDF Plugin 2) Chrome PDF Viewer 3) Native Client – undetected Selenium Jul 04 '20 at 21:38
  • Which values are you looking for? Have you looked at the PluginArray page I linked in my answer? It links to the Plugin page which has the different properties you can access: description, filename, and version. Is that what you're looking for or ? – JeffC Jul 04 '20 at 21:40
  • So what's the overall goal here? You should probably update your question with that. Are you trying to locate a specific plugin, make sure a specific plugin is not installed, etc.? Once we have that, we can better create an answer. – JeffC Jul 04 '20 at 22:03
0

Circular Reference

A circular reference occurs if two separate objects pass references to each other. Circular referencing implies that the 2 objects referencing each other are tightly coupled and changes to one object may need changes in other as well.


NavigatorPlugins.plugins

NavigatorPlugins.plugins returns a PluginArray object, listing the Plugin objects describing the plugins installed in the application. plugins is PluginArray object used to access Plugin objects either by name or as a list of items. The returned value has the length property and supports accessing individual items using bracket notation (e.g. plugins[2]), as well as via item(index) and namedItem("name") methods.


To extract the navigator.plugins properties you can use the following solutions:

  • To get the list of names of the plugins:

    print(driver.execute_script("return Array.from(navigator.plugins).map(({name}) => name);"))
    
    • Console Output:

      ['Chrome PDF Plugin', 'Chrome PDF Viewer', 'Native Client']
      
  • To get the list of filename of the plugins:

    print(driver.execute_script("return Array.from(navigator.plugins).map(({filename}) => filename);"))
    
    • Console Output:

      ['internal-pdf-viewer', 'mhjfbmdgcfjbbpaeojofohoefgiehjai', 'internal-nacl-plugin']
      
  • To get the list of description of the plugins:

    print(driver.execute_script("return Array.from(navigator.plugins).map(({description}) => description);"))
    
    • Console Output:

      ['Portable Document Format', '', '']
      
undetected Selenium
  • 183,867
  • 41
  • 278
  • 352