BinaryModeBuilder.pm 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. package Selenium::BinaryModeBuilder;
  2. # ABSTRACT: Teach a WebDriver how to start its own binary aka no JRE!
  3. use File::Which qw/which/;
  4. use IO::Socket::INET;
  5. use Selenium::Waiter qw/wait_until/;
  6. use Selenium::Firefox::Binary qw/firefox_path setup_firefox_binary_env/;
  7. use Selenium::Firefox::Profile;
  8. use Try::Tiny;
  9. use Moo::Role;
  10. has 'binary_mode' => (
  11. is => 'lazy',
  12. init_arg => undef,
  13. builder => 1,
  14. predicate => 1
  15. );
  16. has 'try_binary' => (
  17. is => 'lazy',
  18. default => sub { 0 },
  19. trigger => sub {
  20. my ($self) = @_;
  21. $self->binary_mode if $self->try_binary;
  22. }
  23. );
  24. sub BUILDARGS {
  25. my ( $class, %args ) = @_;
  26. if ( ! exists $args{remote_server_addr} && ! exists $args{port} ) {
  27. $args{try_binary} = 1;
  28. }
  29. return { %args };
  30. }
  31. sub _build_binary_mode {
  32. my ($self) = @_;
  33. return try {
  34. my $port = start_binary_on_port($self->binary_name, $self->binary_port);
  35. $self->port($port);
  36. return 1;
  37. }
  38. catch {
  39. warn $_;
  40. return 0;
  41. };
  42. }
  43. sub probe_port {
  44. my ($port) = @_;
  45. return IO::Socket::INET->new(
  46. PeerAddr => '127.0.0.1',
  47. PeerPort => $port,
  48. Timeout => 3
  49. );
  50. }
  51. sub start_binary_on_port {
  52. my ($process, $port) = @_;
  53. my $executable = _find_executable($process);
  54. $port = _find_open_port_above($port);
  55. if ($process eq 'firefox') {
  56. setup_firefox_binary_env($port);
  57. }
  58. my $command = _construct_command($executable, $port);
  59. system($command);
  60. my $success = wait_until { probe_port($port) } timeout => 10;
  61. if ($success) {
  62. return $port;
  63. }
  64. else {
  65. die 'Unable to connect to the ' . $executable . ' binary on port ' . $port;
  66. }
  67. }
  68. sub shutdown_binary {
  69. my ($self) = @_;
  70. if ($self->has_binary_mode && $self->binary_mode) {
  71. my $port = $self->port;
  72. my $ua = $self->ua;
  73. $ua->get('127.0.0.1:' . $port . '/wd/hub/shutdown');
  74. }
  75. }
  76. sub _find_executable {
  77. my ($binary) = @_;
  78. if ($binary eq 'firefox') {
  79. return firefox_path();
  80. }
  81. else {
  82. my $executable = which($binary);
  83. if (not defined $executable) {
  84. die qq(Unable to find the $binary binary in your \$PATH. We'll try falling back to standard Remote Driver);
  85. }
  86. else {
  87. return $executable;
  88. }
  89. }
  90. }
  91. sub _construct_command {
  92. my ($executable, $port) = @_;
  93. my %args;
  94. if ($executable =~ /chromedriver$/) {
  95. %args = (
  96. port => $port,
  97. 'url-base' => 'wd/hub'
  98. );
  99. }
  100. elsif ($executable =~ /phantomjs$/) {
  101. %args = (
  102. webdriver => '127.0.0.1:' . $port
  103. );
  104. }
  105. elsif ($executable =~ /firefox/i) {
  106. $executable .= ' -no-remote ';
  107. }
  108. my @args = map { '--' . $_ . '=' . $args{$_} } keys %args;
  109. return join(' ', ($executable, @args, '> /dev/null 2>&1 &') );
  110. }
  111. sub _find_open_port_above {
  112. my ($port) = @_;
  113. my $free_port = wait_until {
  114. if ( probe_port($port) ) {
  115. $port++;
  116. return 0;
  117. }
  118. else {
  119. return $port;
  120. }
  121. };
  122. return $free_port;
  123. }
  124. 1;