Skip to content

Commit

Permalink
Added BrowserFactory::connectToBrowser #42
Browse files Browse the repository at this point in the history
  • Loading branch information
gsouf committed Oct 13, 2018
1 parent 100b4cf commit 7ce409b
Show file tree
Hide file tree
Showing 7 changed files with 123 additions and 26 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
* Features:
* Added option ``keepAlive`` for browser factory.
* Added methods ``BrowserProcess::getSocketUri`` and ``ProcessAwareBrowser::getSocketUri``
* Removed unused option ``debug``
* Added ``BrowserFactory::connectToBrowser``
* Bug fixes:
* none

Expand Down
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,6 @@ Here are the options available for the browser factory:
|--------------------|---------------|--------------------------------------------------------------------------------------------------|
| connectionDelay | 0 | Delay to apply between each operation for debugging purposes |
| customFlags | none | Array of flags to pass to the command line. Eg: ``['--option1', '--option2=someValue']`` |
| debug | false | Allows to enable debug mode |
| debugLogger | null | A string (e.g "php://stdout"), or resource, or PSR-3 logger instance to print debug messages |
| enableImages | true | Toggles loading of images |
| headless | true | Enable or disable headless mode |
Expand Down
3 changes: 3 additions & 0 deletions src/Browser.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ public function __construct(Connection $connection)
->debug('✘ target(' . $params['targetId'] . ') was destroyed and unreferenced.');
}
});

// enable target discovery
$connection->sendMessageSync(new Message('Target.setDiscoverTargets', ['discover' => true]));
}

/**
Expand Down
5 changes: 1 addition & 4 deletions src/Browser/BrowserProcess.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ class BrowserProcess implements LoggerAwareInterface

/**
* BrowserProcess constructor.
* @param LoggerInterface $logger
* @param LoggerInterface|null $logger
*/
public function __construct(LoggerInterface $logger = null)
{
Expand Down Expand Up @@ -146,9 +146,6 @@ public function start($binaries, $options)

// create browser instance
$this->browser = new ProcessAwareBrowser($connection, $this);

// enable target discovery
$connection->sendMessageSync(new Message('Target.setDiscoverTargets', ['discover' => true]));
}

/**
Expand Down
98 changes: 80 additions & 18 deletions src/BrowserFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@
use Apix\Log\Logger\Stream as StreamLogger;
use HeadlessChromium\Browser\BrowserProcess;
use HeadlessChromium\Browser\ProcessAwareBrowser;
use HeadlessChromium\Communication\Connection;
use HeadlessChromium\Exception\BrowserConnectionFailed;
use Symfony\Component\Process\Process;
use Wrench\Exception\HandshakeException;

class BrowserFactory
{
Expand Down Expand Up @@ -37,9 +40,7 @@ public function __construct(string $chromeBinaries = null)
* @param array $options options for browser creation:
* - connectionDelay: amount of time in seconds to slows down connection for debugging purposes (default: none)
* - customFlags: array of custom flag to flags to pass to the command line
* - debug: toggles the debug mode that allows to print additional details (default: false)
* - debugLogger: resource string ("php://stdout"), resource or psr-3 logger instance (default: none)
* enabling debug logger will also enable debug mode.
* - enableImages: toggle the loading of images (default: true)
* - headless: whether chrome should be started headless (default: true)
* - ignoreCertificateErrors: set chrome to ignore ssl errors
Expand All @@ -56,24 +57,13 @@ public function __construct(string $chromeBinaries = null)
public function createBrowser(array $options = []): ProcessAwareBrowser
{

// prepare logger
$logger = $options['debugLogger'] ?? null;
// create logger from options
$logger = self::createLogger($options);

// create logger from string name or resource
if (is_string($logger) || is_resource($logger)) {
$logger = new StreamLogger($logger);
$options['debug'] = true;
}

$debugEnabled = $options['debug'] ?? false;

// log
if ($debugEnabled) {
// log chrome version
if ($logger) {
$chromeVersion = $this->getChromeVersion();

if ($logger) {
$logger->debug('Factory: chrome version: ' . $chromeVersion);
}
$logger->debug('Factory: chrome version: ' . $chromeVersion);
}

// create browser process
Expand Down Expand Up @@ -114,4 +104,76 @@ public function getChromeVersion()

return trim($process->getOutput());
}

/**
* Connects to an existing browser using it's web socket uri.
*
* usage:
*
* ```
* $browserFactory = new BrowserFactory();
* $browser = $browserFactory->createBrowser();
*
* $uri = $browser->getSocketUri();
*
* $existingBrowser = BrowserFactory::connectToBrowser($uri);
* ```
*
* @param string $uri
* @param array $options options when creating the connection to the browser:
* - connectionDelay: amount of time in seconds to slows down connection for debugging purposes (default: none)
* - debugLogger: resource string ("php://stdout"), resource or psr-3 logger instance (default: none)
* - sendSyncDefaultTimeout: maximum time in ms to wait for synchronous messages to send (default 3000 ms)
*
* @return Browser
* @throws BrowserConnectionFailed
*/
public static function connectToBrowser(string $uri, array $options = []): Browser
{
$logger = self::createLogger($options);

if ($logger) {
$logger->debug('Browser Factory: connecting using ' . $uri);
}

// connect to browser
$connection = new Connection($uri, $logger, $options['sendSyncDefaultTimeout'] ?? 3000);

// try to connect
try {
$connection->connect();
} catch (HandshakeException $e) {
throw new BrowserConnectionFailed('Invalid socket uri', 0, $e);
}

// make sure it is connected
if (!$connection->isConnected()) {
throw new BrowserConnectionFailed('Cannot connect to the browser, make sure it was not closed');
}

// connection delay
if (array_key_exists('connectionDelay', $options)) {
$connection->setConnectionDelay($options['connectionDelay']);
}

return new Browser($connection);
}

/**
* Create a logger instance from given options
* @param $options
* @return StreamLogger|null
*/
private static function createLogger($options)
{
// prepare logger
$logger = $options['debugLogger'] ?? null;

// create logger from string name or resource
if (is_string($logger) || is_resource($logger)) {
$logger = new StreamLogger($logger);
}

return $logger;
}
}
11 changes: 11 additions & 0 deletions src/Exception/BrowserConnectionFailed.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php
/**
* @license see LICENSE
*/

namespace HeadlessChromium\Exception;

class BrowserConnectionFailed extends \Exception
{

}
29 changes: 26 additions & 3 deletions test/suites/BrowserFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,22 @@
namespace HeadlessChromium\Test;

use HeadlessChromium\BrowserFactory;
use HeadlessChromium\Communication\Target;

/**
* @covers \HeadlessChromium\BrowserFactory
* @covers \HeadlessChromium\Browser\BrowserProcess
*/
class BrowserFactoryTest extends BaseTestCase
{
public function testBrowserFactory()
{
$factory = new BrowserFactory();

$browser = $factory->createBrowser();

$this->assertRegExp('#^ws://#', $browser->getSocketUri());
}

public function testWindowSizeOption()
{
Expand Down Expand Up @@ -44,12 +53,26 @@ public function testUserAgentOption()
$this->assertEquals('foo bar baz', $response);
}

public function testBrowserFactory()
public function testConnectToBrowser()
{
// create a browser
$factory = new BrowserFactory();

$browser = $factory->createBrowser();

$this->assertRegExp('#^ws://#', $browser->getSocketUri());
// TODO test existing pages propagation

// create a new connectionn to the existing browser
$browser2 = BrowserFactory::connectToBrowser($browser->getSocketUri());

// create a page on the first browser after 2d connection
$page2 = $browser->createPage();
$page2TargetId = $page2->getSession()->getTargetId();

// update 2d browser
$browser2->getConnection()->readData();

// make sure 2nd browser received the new page
$target = $browser2->getTarget($page2TargetId);
$this->assertInstanceOf(Target::class, $target);
}
}

0 comments on commit 7ce409b

Please sign in to comment.