0

There are plenty examples using Selenium Python to handle shadow DOM. I'd like to do the same in Perl.

Perl's Selenium::Remote::Driver doesn't have shadow DOM support, but I should be able to do it through JavaScript. I got my inspiration from accessing-shadow-dom-tree-with-selenium.

The following is my code in Perl

#!/usr/bin/env perl use Selenium::Chrome; my $driver = Selenium::Chrome->new ( startup_timeout => 60, custom_args => "--log-path=/tmp/selenium_chromedriver", logfile => "/tmp/selenium_chromedriver2", debug_on => 1, extra_capabilities => { 'goog:chromeOptions' => { args => [ '--no-sandbox', '--disable-dev-shm-usage', '--window-size=1260,720', '--user-data-dir=/tmp/selenium_chrome', ], }, }, ); $driver->get("chrome-search://local-ntp/local-ntp.html"); # chrome new tab my $shadow_host = $driver->find_element("html/body/ntp-app", "xpath"); my $shadow_root = $driver->execute_script('return arguments[0].shadowRoot', $shadow_host); for my $e ( @{$shadow_root->find_elements(':host > *', 'css')} ) { # error: Can't call method "find_elements" on unblessed reference print "found\n"; } $driver->shutdown_binary(); 

But I got error: Can't call method "find_elements" on unblessed reference.

How can I overcome this error?

Thank you for any help.

  • My environment is: ubuntu 18, Perl 5.26, Selenium:Chrome 1.46, Chrome 99, chromedriver 99.
  • The same mechanism is tested working with Python 3.8.5.
  • Why I am not using Python? because the server in my work place only has Perl, no Python 3.
2
  • FYI I haven't seen any perl questions here, so you probably will need to switch to python or figure it out yourself Commented Sep 16, 2022 at 7:24
  • I kind figured it out. I just need to write a custom package to customize find_element() and find_elements(), like Python did Commented Sep 16, 2022 at 15:22

1 Answer 1

1

the following code works

#!/usr/bin/env perl use Selenium::Chrome; my $driver = Selenium::Chrome->new ( startup_timeout => 60, custom_args => "--log-path=/tmp/selenium_chromedriver", logfile => "/tmp/selenium_chromedriver2", debug_on => 1, extra_capabilities => { 'goog:chromeOptions' => { args => [ '--no-sandbox', '--disable-dev-shm-usage', '--window-size=1260,720', '--user-data-dir=/tmp/selenium_chrome', ], }, }, ); $driver->get("chrome-search://local-ntp/local-ntp.html"); # chrome new tab my $shadow_host = $driver->find_element("html/body/ntp-app", "xpath"); package MyShadow { sub new { my ($class, %attrs) = @_; my $shadow_root = $attrs{driver}->execute_script('return arguments[0].shadowRoot', $attrs{shadow_host}); return undef if ! $shadow_root; $attrs{shadow_root} = $shadow_root; bless \%attrs, $class; } sub find_element { my ($self, $target, $scheme) = @_; die "scheme=$scheme is not supported. Only css is supported" if $scheme ne 'css'; return $self->{driver}->execute_script( "return arguments[0].querySelector(arguments[1])", $self->{shadow_root}, $target ); } sub find_elements { my ($self, $target, $scheme) = @_; die "scheme=$scheme is not supported. Only css is supported" if $scheme ne 'css'; return $self->{driver}->execute_script( "return arguments[0].querySelectorAll(arguments[1])", $self->{shadow_root}, $target ); } }; my $shadow_driver = MyShadow->new(driver=>$driver, shadow_host=>$shadow_host); if ($shadow_driver) { for my $e ( @{$shadow_driver->find_elements(':host > *', 'css')} ) { print "found\n"; } } $driver->shutdown_binary(); 

Key points:

  • For Selenium, no matter Python or Perl, they are wrappers to javascript. As long as you get correct javascript, you can do whatever you want.

  • For Shadow driver, all you need to implement is the find_element() and find_elements().

  • I only implemented 'css', no 'xpath', because that's what Python does as of 2022/09/19.

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.