Is it possible to access elements within a Shadow DOM using Selenium/Chrome webdriver?
Using the normal element search methods doesn\'t work, as is to be expected.
I am using C# and Selenium and managed to find an element inside a nestled shadow DOM using java script. This is my html tree:
html tree
I want the url on the last line and to get it I first select the "downloads-manager" tag and then the first shadow root right below it. Once inside the shadow root I want to find the element closest to the next shadow root. That element is "downloads-item". With that selected I can enter the second shadow root. From there I select the img item containing the url by id = "file-icon". At last I can get the attribute "src" which contains the url I am seeking.
The two lines of C# code that does the trick:
IJavaScriptExecutor jse2 = (IJavaScriptExecutor)_driver;
var pdfUrl = jse2.ExecuteScript("return document.querySelector('downloads-manager').shadowRoot.querySelector('downloads-item').shadowRoot.getElementById('file-icon').getAttribute('src')");
This worked for me (using selenium javascript bindings):
driver.executeScript("return $('body /deep/ <#selector>')")
That returns the element(s) you're looking for.
Normally you'd do this:
element = webdriver.find_element_by_css_selector(
'my-custom-element /deep/ .this-is-inside-my-custom-element')
And hopefully that'll continue to work.
However, note that /deep/
and ::shadow
are deprecated (and not implemented in browsers other than Opera and Chrome). There's much talk about allowing them in the static profile. Meaning, querying for them will work, but not styling.
If don't want to rely on /deep/
or ::shadow
because their futures are a bit uncertain, or because you want to work better cross-browser or because you hate deprecation warnings, rejoice as there's another way:
# Get the shadowRoot of the element you want to intrude in on,
# and then use that as your root selector.
shadow_root = webdriver.execute_script('''
return document.querySelector(
'my-custom-element').shadowRoot;
''')
element = shadow_root.find_element_by_css_selector(
'.this-is-inside-my-custom-element')
More about this:
It should also be noted that the Selenium binary Chrome driver now supports Shadow DOM (since Jan 28, 2015) : http://chromedriver.storage.googleapis.com/2.14/notes.txt
For getting the filename of the latest downloaded file in Chrome
def get_downloaded_file(self):
filename = self._driver.execute_script("return document.querySelector('downloads-manager').shadowRoot.querySelector('#downloadsList downloads-item').shadowRoot.querySelector('div#content #file-link').text")
return filename
Usage:
driver.get_url('chrome://downloads')
filename = driver.get_downloaded_file()
And for configuring the option for setting the default download directory in selenium for chrome browser, where the corresponding file could be gotten:
..
chrome_options = webdriver.ChromeOptions()
..
prefs = {'download.default_directory': '/desired-path-to-directory'} # unix
chrome_options.add_experimental_option('prefs', prefs)
..
Unfortunately it looks like the webdriver spec does not support this yet.
My snooping uncovered :
http://www.w3.org/TR/webdriver/#switching-to-hosted-shadow-doms
https://groups.google.com/forum/#!msg/selenium-developers/Dad2KZsXNKo/YXH0e6eSHdAJ