javascript - Accessing ShadowRoot via Selenium in Firefox returns JavascriptException: Cyclic object value - Stack Overflow

admin2025-04-19  1

I'm trying to automate the www.freeinvaders version of Space Invaders using Python and Selenium. The actual game works through a HTML5 canvas element, which is wrapped in a shadow-root.

Using the answer to this question, I'm attempting to expand the shadow-root, so I can click the canvas and 'play' the game.

My current code:

  def expand_shadow_element(element):
     shadowRoot = browser.execute_script('return arguments[0].shadowRoot', element)
     return shadowRoot
  

  browser = webdriver.Firefox()
  browser.get("/")

  #wait for element to load

  el = WebDriverWait(browser, timeout=20).until(lambda d:d.find_element_by_tag_name("ruffle-player"))
  time.sleep(5)

  #expand the shadowroot and click the canvas
  host = browser.find_element_by_tag_name("ruffle-player")
  shadowRoot = expand_shadow_element(host)
  canvas = shadowRoot.find_element_by_tag_name("canvas")
  canvas.click()

The HTML structure of the page is like this: (abridged for legibility)

<!DOCTYPE html> 
<html lang="en-US">
<head>

<title>Free Invaders</title>

</head>
<body>
<div id='full-page-container'>  
<main>
  <div id="game-container">
       
    <ruffle-player>
  #shadow-root (open)
  <div id="container" style="visibility: visible;">
      
    <canvas width="1600" height="760" style="touch-action: none; cursor: auto;"></canvas>
    </div><!--container-->
  </ruffle-player>
  </div><!--game-container-->

</div><!--fullpagecontainer-->
</body>

When running the above Pyhton script, it fails on this line:

shadowRoot = browser.execute_script('return arguments[0].shadowRoot', element)

with a Javascript error:

seleniummon.exceptions.JavascriptException: Message: Cyclic object value

I know that error is supposed to mean there is a self-referencing item in the returned JSON string, but that shouldn't be the case here.

Can anyone help me by explaining why this error occurs and what course of action might alleviate the issue?

I'm using Python 3.8.5, Selenium 3.141.0 and Firefox 86.0. All of this runs on Linux Mint 20.1.

Edit I have also attempted the alternative Javascript:

shadowRoot = browser.execute_script('document.querySelector("ruffle-player").shadowRoot')

But that just returns another error:

AttributeError: 'NoneType' object has no attribute 'find_element_by_tag_name'

Which indicates it doesn't even find any objects.

I'm trying to automate the www.freeinvaders version of Space Invaders using Python and Selenium. The actual game works through a HTML5 canvas element, which is wrapped in a shadow-root.

Using the answer to this question, I'm attempting to expand the shadow-root, so I can click the canvas and 'play' the game.

My current code:

  def expand_shadow_element(element):
     shadowRoot = browser.execute_script('return arguments[0].shadowRoot', element)
     return shadowRoot
  

  browser = webdriver.Firefox()
  browser.get("http://www.freeinvaders/")

  #wait for element to load

  el = WebDriverWait(browser, timeout=20).until(lambda d:d.find_element_by_tag_name("ruffle-player"))
  time.sleep(5)

  #expand the shadowroot and click the canvas
  host = browser.find_element_by_tag_name("ruffle-player")
  shadowRoot = expand_shadow_element(host)
  canvas = shadowRoot.find_element_by_tag_name("canvas")
  canvas.click()

The HTML structure of the page is like this: (abridged for legibility)

<!DOCTYPE html> 
<html lang="en-US">
<head>

<title>Free Invaders</title>

</head>
<body>
<div id='full-page-container'>  
<main>
  <div id="game-container">
       
    <ruffle-player>
  #shadow-root (open)
  <div id="container" style="visibility: visible;">
      
    <canvas width="1600" height="760" style="touch-action: none; cursor: auto;"></canvas>
    </div><!--container-->
  </ruffle-player>
  </div><!--game-container-->

</div><!--fullpagecontainer-->
</body>

When running the above Pyhton script, it fails on this line:

shadowRoot = browser.execute_script('return arguments[0].shadowRoot', element)

with a Javascript error:

selenium.mon.exceptions.JavascriptException: Message: Cyclic object value

I know that error is supposed to mean there is a self-referencing item in the returned JSON string, but that shouldn't be the case here.

Can anyone help me by explaining why this error occurs and what course of action might alleviate the issue?

I'm using Python 3.8.5, Selenium 3.141.0 and Firefox 86.0. All of this runs on Linux Mint 20.1.

Edit I have also attempted the alternative Javascript:

shadowRoot = browser.execute_script('document.querySelector("ruffle-player").shadowRoot')

But that just returns another error:

AttributeError: 'NoneType' object has no attribute 'find_element_by_tag_name'

Which indicates it doesn't even find any objects.

Share Improve this question edited Apr 17, 2021 at 0:14 Tijmen asked Apr 17, 2021 at 0:01 TijmenTijmen 6411 gold badge10 silver badges34 bronze badges 3
  • Ok so apparently this does work in Chrome, which I reluctantly installed after staring at this for days. That's a bit of an anticlimax, as it's not so much something wrong with my code as much as it is (apparently) a bug in either Firefox or Geckodriver. Nonetheless I'm going to leave this question open at least until the bounty expires to see if anyone has a solution for Firefox specifically. – Tijmen Commented Apr 19, 2021 at 19:31
  • I am not able to access the webpage that you shared on my laptop due to some firewalls yet on my end but, you can refer to stackoverflow./questions/56380091/… to understand the shadow-root and it's implementation along with the options to work on them. – supputuri Commented Apr 21, 2021 at 1:07
  • Thanks, but that answer, although informative, ends up referring to the solution I'm already implementing in my code. As noted in my ment above, the solution works in Chrome with Chromedriver, but not in Firefox with Geckodriver. – Tijmen Commented Apr 21, 2021 at 19:24
Add a ment  | 

2 Answers 2

Reset to default 5 +50

It seems that this is a known issue filed on bugzilla.

The geckodriver devs also say that the WebDriver spec needs to be updated. See: w3c/webdriver#350.

However, CAVAh has found and posted a workaround for this issue here.

Following the advice to return the children of shadowroot return arguments[0].shadowRoot.children it finds 4 elements:

[<selenium.webdriver.firefox.webelement.FirefoxWebElement (session="1dd0fedf-1a8c-42f2-a4de-0ed7df478212", element="252a6352-4fe0-409d-a626-18456a973da5")>, 
<selenium.webdriver.firefox.webelement.FirefoxWebElement (session="1dd0fedf-1a8c-42f2-a4de-0ed7df478212", element="47d76aed-5f44-4933-9718-53267a6417bf")>, 
<selenium.webdriver.firefox.webelement.FirefoxWebElement (session="1dd0fedf-1a8c-42f2-a4de-0ed7df478212", element="d3fcad48-7cbb-4de3-a247-49d7d227e982")>, 
<selenium.webdriver.firefox.webelement.FirefoxWebElement (session="1dd0fedf-1a8c-42f2-a4de-0ed7df478212", element="abb5012e-79bf-494b-b5f2-ff28dfecab0f")>]

After inspecting those, it looks like the third element contains the canvas and can be clicked on.

The following code seems to work for me:

def expand_shadow_element(element):
    # return a list of elements
    shadowRoot = browser.execute_script('return arguments[0].shadowRoot.children', element)
    return shadowRoot


browser = webdriver.Firefox()
browser.get("http://www.freeinvaders/")

#wait for element to load

el = WebDriverWait(browser, timeout=20).until(lambda d:d.find_element_by_tag_name("ruffle-player"))
time.sleep(5)

#expand the shadowroot and click the canvas
host = browser.find_element_by_tag_name("ruffle-player")
shadowRoot = expand_shadow_element(host)

canvas = shadowRoot[2].find_element_by_tag_name("canvas")
canvas.click()

Should now be fixed in firefox 111

see https://bugzilla.mozilla/show_bug.cgi?id=1764594

You can download firefox version 111 here:

https://download-installer.cdn.mozilla/pub/firefox/releases/111.0/

转载请注明原文地址:http://conceptsofalgorithm.com/Algorithm/1745002315a279303.html

最新回复(0)