1
0

CanStartBinary.pm 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. package Selenium::CanStartBinary;
  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 Moo::Role;
  9. has 'binary_mode' => (
  10. is => 'lazy',
  11. init_arg => undef,
  12. builder => 1,
  13. predicate => 1
  14. );
  15. has 'try_binary' => (
  16. is => 'lazy',
  17. default => sub { 0 },
  18. trigger => sub {
  19. my ($self) = @_;
  20. $self->binary_mode if $self->try_binary;
  21. }
  22. );
  23. sub BUILDARGS {
  24. my ( $class, %args ) = @_;
  25. if ( ! exists $args{remote_server_addr} && ! exists $args{port} ) {
  26. $args{try_binary} = 1;
  27. # Windows may throw a fit about invalid pointers if we try to
  28. # connect to localhost instead of 127.1
  29. $args{remote_server_addr} = '127.0.0.1';
  30. }
  31. return { %args };
  32. }
  33. sub _build_binary_mode {
  34. my ($self) = @_;
  35. my $port = $self->start_binary_on_port(
  36. $self->binary_name,
  37. $self->binary_port
  38. );
  39. $self->port($port);
  40. return 1;
  41. }
  42. sub probe_port {
  43. my ($port) = @_;
  44. return IO::Socket::INET->new(
  45. PeerAddr => '127.0.0.1',
  46. PeerPort => $port,
  47. Timeout => 3
  48. );
  49. }
  50. sub start_binary_on_port {
  51. my ($self, $process, $port) = @_;
  52. my $executable = $self->_find_executable($process);
  53. $port = _find_open_port_above($port);
  54. if ($process eq 'firefox') {
  55. setup_firefox_binary_env($port);
  56. }
  57. my $command = _construct_command($executable, $port);
  58. system($command);
  59. my $success = wait_until { probe_port($port) } timeout => 10;
  60. if ($success) {
  61. return $port;
  62. }
  63. else {
  64. die 'Unable to connect to the ' . $executable . ' binary on port ' . $port;
  65. }
  66. }
  67. sub shutdown_binary {
  68. my ($self) = @_;
  69. if ($self->has_binary_mode && $self->binary_mode) {
  70. my $port = $self->port;
  71. my $ua = $self->ua;
  72. $ua->get('127.0.0.1:' . $port . '/wd/hub/shutdown');
  73. }
  74. }
  75. sub _find_executable {
  76. my ($self, $binary) = @_;
  77. if ($self->has_binary_path) {
  78. if (-x $self->binary_path) {
  79. return $self->binary_path;
  80. }
  81. else {
  82. die 'The binary at "' . $self->binary_path . '" is not executable. Fix the path or chmod +x it as needed.';
  83. }
  84. }
  85. if ($binary eq 'firefox') {
  86. return firefox_path();
  87. }
  88. else {
  89. my $executable = which($binary);
  90. if (not defined $executable) {
  91. warn qq(Unable to find the $binary binary in your \$PATH. We'll try falling back to standard Remote Driver);
  92. }
  93. else {
  94. return $executable;
  95. }
  96. }
  97. }
  98. sub _construct_command {
  99. my ($executable, $port) = @_;
  100. my %args;
  101. if ($executable =~ /chromedriver(\.exe)?$/i) {
  102. %args = (
  103. port => $port,
  104. 'url-base' => 'wd/hub'
  105. );
  106. }
  107. elsif ($executable =~ /phantomjs(\.exe)?$/i) {
  108. %args = (
  109. webdriver => '127.0.0.1:' . $port
  110. );
  111. }
  112. elsif ($executable =~ /firefox/i) {
  113. $executable .= ' -no-remote ';
  114. }
  115. my @args = map { '--' . $_ . '=' . $args{$_} } keys %args;
  116. # Handle Windows vs Unix discrepancies for invoking shell commands
  117. my ($prefix, $suffix) = (_command_prefix(), _command_suffix());
  118. return join(' ', ($prefix, $executable, @args, $suffix) );
  119. }
  120. sub _command_prefix {
  121. if ($^O eq 'MSWin32') {
  122. return 'start /MAX '
  123. }
  124. else {
  125. return '';
  126. }
  127. }
  128. sub _command_suffix {
  129. if ($^O eq 'MSWin32') {
  130. return ' > /nul 2>&1 ';
  131. }
  132. else {
  133. # TODO: allow users to specify whether & where they want
  134. # driver output to go
  135. return ' > /dev/null 2>&1 &';
  136. }
  137. }
  138. sub _find_open_port_above {
  139. my ($port) = @_;
  140. my $free_port = wait_until {
  141. if ( probe_port($port) ) {
  142. $port++;
  143. return 0;
  144. }
  145. else {
  146. return $port;
  147. }
  148. };
  149. return $free_port;
  150. }
  151. 1;