Commit 7c227270 authored by Soufiane Ghzal's avatar Soufiane Ghzal
Browse files

Implement Page::close

parent f0cb8b29
......@@ -13,6 +13,17 @@
--------------
## 0.1.4
> *2018-04-27*
> Description
* Features:
* Add Page::close
--------------
## 0.1.3
> *2018-04-26*
......
......@@ -39,6 +39,20 @@ class Browser
$session
);
});
$this->connection->on(Connection::EVENT_TARGET_DESTROYED, function (array $params) {
$target = $this->getTarget($params['targetId']);
if ($target) {
// remove the target
unset($this->targets[$params['targetId']]);
$target->destroy();
$this->connection
->getLogger()
->debug('✘ target(' . $params['targetId'] . ') was destroyed and unreferenced.');
}
});
}
/**
......@@ -76,11 +90,10 @@ class Browser
// todo handle error
// make sure target was created (via Target.targetCreated event)
if (!array_key_exists($targetId, $this->targets)) {
throw new \RuntimeException('Target could not be created for page');
$target = $this->getTarget($targetId);
if (!$target) {
throw new \RuntimeException('Target could not be created for page.');
}
$target = $this->targets[$targetId];
// get initial frame tree
$frameTreeResponse = $target->getSession()->sendMessageSync(new Message('Page.getFrameTree'));
......@@ -101,4 +114,17 @@ class Browser
return $page;
}
/**
* @param $targetId
* @return Target|null
*/
public function getTarget($targetId)
{
// make sure target was created (via Target.targetCreated event)
if (!array_key_exists($targetId, $this->targets)) {
return null;
}
return $this->targets[$targetId];
}
}
......@@ -22,6 +22,7 @@ class Connection extends EventEmitter implements LoggerAwareInterface
{
const EVENT_TARGET_CREATED = 'method:Target.targetCreated';
const EVENT_TARGET_DESTROYED = 'method:Target.targetDestroyed';
use LoggerAwareTrait;
......@@ -88,6 +89,14 @@ class Connection extends EventEmitter implements LoggerAwareInterface
$this->wsClient = $socketClient;
}
/**
* @return LoggerInterface
*/
public function getLogger(): LoggerInterface
{
return $this->logger;
}
/**
* Set the delay to apply everytime before data are sent
* @param $delay
......@@ -226,6 +235,11 @@ class Connection extends EventEmitter implements LoggerAwareInterface
$this->sessions[$sessionId] = $session;
$session->on('destroyed', function () use ($sessionId) {
$this->logger->debug('✘ session(' . $sessionId . ') was destroyed and unreferenced.');
unset($this->sessions[$sessionId]);
});
return $session;
}
......
......@@ -8,6 +8,7 @@ namespace HeadlessChromium\Communication;
use Evenement\EventEmitter;
use HeadlessChromium\Exception\CommunicationException;
use HeadlessChromium\Exception\NoResponseAvailable;
use HeadlessChromium\Exception\TargetDestroyed;
class Session extends EventEmitter
{
......@@ -27,6 +28,17 @@ class Session extends EventEmitter
*/
protected $connection;
/**
* @var bool
*/
protected $destroyed = false;
/**
* Session constructor.
* @param string $targetId
* @param string $sessionId
* @param Connection $connection
*/
public function __construct(string $targetId, string $sessionId, Connection $connection)
{
$this->sessionId = $sessionId;
......@@ -41,6 +53,10 @@ class Session extends EventEmitter
*/
public function sendMessage(Message $message): SessionResponseReader
{
if ($this->destroyed) {
throw new TargetDestroyed('The session was destroyed.');
}
$topResponse = $this->connection->sendMessage(new Message('Target.sendMessageToTarget', [
'message' => (string) $message,
'sessionId' => $this->getSessionId()
......@@ -89,6 +105,23 @@ class Session extends EventEmitter
*/
public function getConnection()
{
if ($this->destroyed) {
throw new TargetDestroyed('The session was destroyed.');
}
return $this->connection;
}
/**
* Marks the session as destroyed
* @internal
*/
public function destroy()
{
if ($this->destroyed) {
throw new TargetDestroyed('The session was already destroyed.');
}
$this->emit('destroyed');
$this->connection = null;
$this->removeAllListeners();
}
}
......@@ -5,6 +5,8 @@
namespace HeadlessChromium\Communication;
use HeadlessChromium\Exception\TargetDestroyed;
class Target
{
/**
......@@ -17,6 +19,8 @@ class Target
*/
protected $session;
protected $destroyed = false;
/**
* Target constructor.
* @param array $targetInfo
......@@ -33,6 +37,31 @@ class Target
*/
public function getSession(): Session
{
if ($this->destroyed) {
throw new TargetDestroyed('The target was destroyed.');
}
return $this->session;
}
/**
* Marks the target as destroyed
* @internal
*/
public function destroy()
{
if ($this->destroyed) {
throw new TargetDestroyed('The target was already destroyed.');
}
$this->session->destroy();
$this->session = null;
$this->destroyed = true;
}
/**
* @return bool
*/
public function isDestroyed(): bool
{
return $this->destroyed;
}
}
<?php
/**
* @license see LICENSE
*/
namespace HeadlessChromium\Exception;
class TargetDestroyed extends \RuntimeException
{
}
......@@ -12,6 +12,7 @@ use HeadlessChromium\Communication\Target;
use HeadlessChromium\Exception\CommunicationException;
use HeadlessChromium\Exception\NoResponseAvailable;
use HeadlessChromium\Exception\CommunicationException\ResponseHasError;
use HeadlessChromium\Exception\TargetDestroyed;
use HeadlessChromium\PageUtils\PageEvaluation;
use HeadlessChromium\PageUtils\PageNavigation;
use HeadlessChromium\PageUtils\PageScreenshot;
......@@ -27,18 +28,35 @@ class Page
*/
protected $target;
/**
* @var FrameManager
*/
protected $frameManager;
public function __construct(Target $target, array $frameTree)
{
$this->target = $target;
$this->frameManager = new FrameManager($this, $frameTree);
}
/**
* @return FrameManager
*/
public function getFrameManager(): FrameManager
{
$this->assertNotClosed();
return $this->frameManager;
}
/**
* Get the session this page is attached to
* @return Session
*/
public function getSession(): Session
{
$this->assertNotClosed();
return $this->target->getSession();
}
......@@ -51,6 +69,8 @@ class Page
*/
public function navigate($url)
{
$this->assertNotClosed();
// make sure latest loaderId was pulled
$this->getSession()->getConnection()->readData();
......@@ -88,6 +108,8 @@ class Page
*/
public function evaluate(string $expression)
{
$this->assertNotClosed();
$currentLoaderId = $this->frameManager->getMainFrame()->getLatestLoaderId();
$reader = $this->getSession()->sendMessage(
new Message(
......@@ -113,6 +135,8 @@ class Page
*/
public function getCurrentLifecycle()
{
$this->assertNotClosed();
$this->getSession()->getConnection()->readData();
return $this->frameManager->getMainFrame()->getLifeCycle();
}
......@@ -133,6 +157,8 @@ class Page
*/
public function hasLifecycleEvent(string $event): bool
{
$this->assertNotClosed();
return array_key_exists($event, $this->getCurrentLifecycle());
}
......@@ -146,6 +172,8 @@ class Page
*/
public function waitForReload($eventName = Page::LOAD, $timeout = 30000, $loaderId = null)
{
$this->assertNotClosed();
if (!$loaderId) {
$loaderId = $loader = $this->frameManager->getMainFrame()->getLatestLoaderId();
}
......@@ -197,6 +225,8 @@ class Page
*/
public function screenshot(array $options = []): PageScreenshot
{
$this->assertNotClosed();
$screenshotOptions = [];
// get format
......@@ -265,4 +295,35 @@ class Page
return new PageScreenshot($responseReader);
}
/**
* Request to close the page
* @throws CommunicationException
*/
public function close()
{
$this->assertNotClosed();
$this->getSession()
->getConnection()
->sendMessage(
new Message(
'Target.closeTarget',
['targetId' => $this->getSession()->getTargetId()]
)
);
// TODO return close waiter
}
/**
* Throws if the page was closed
*/
private function assertNotClosed()
{
if ($this->target->isDestroyed()) {
throw new TargetDestroyed('The page was closed and is not available anymore.');
}
}
}
......@@ -41,7 +41,7 @@ class PageNavigation
public function __construct(Page $page, string $previousLoaderId, string $currentLoaderId)
{
$this->page = $page;
$this->frame = $this->page->frameManager->getMainFrame();
$this->frame = $this->page->getFrameManager()->getMainFrame();
$this->previousLoaderId = $previousLoaderId;
$this->currentLoaderId = $currentLoaderId;
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment