Skip to content

Commit 7c22727

Browse files
committed
Implement Page::close
1 parent f0cb8b2 commit 7c22727

File tree

8 files changed

+190
-5
lines changed

8 files changed

+190
-5
lines changed

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,17 @@
1313

1414
--------------
1515

16+
## 0.1.4
17+
18+
> *2018-04-27*
19+
20+
> Description
21+
22+
* Features:
23+
* Add Page::close
24+
25+
--------------
26+
1627
## 0.1.3
1728

1829
> *2018-04-26*

src/Browser.php

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,20 @@ public function __construct(Connection $connection)
3939
$session
4040
);
4141
});
42+
43+
$this->connection->on(Connection::EVENT_TARGET_DESTROYED, function (array $params) {
44+
45+
$target = $this->getTarget($params['targetId']);
46+
47+
if ($target) {
48+
// remove the target
49+
unset($this->targets[$params['targetId']]);
50+
$target->destroy();
51+
$this->connection
52+
->getLogger()
53+
->debug('✘ target(' . $params['targetId'] . ') was destroyed and unreferenced.');
54+
}
55+
});
4256
}
4357

4458
/**
@@ -76,11 +90,10 @@ public function createPage(): Page
7690

7791
// todo handle error
7892

79-
// make sure target was created (via Target.targetCreated event)
80-
if (!array_key_exists($targetId, $this->targets)) {
81-
throw new \RuntimeException('Target could not be created for page');
93+
$target = $this->getTarget($targetId);
94+
if (!$target) {
95+
throw new \RuntimeException('Target could not be created for page.');
8296
}
83-
$target = $this->targets[$targetId];
8497

8598
// get initial frame tree
8699
$frameTreeResponse = $target->getSession()->sendMessageSync(new Message('Page.getFrameTree'));
@@ -101,4 +114,17 @@ public function createPage(): Page
101114

102115
return $page;
103116
}
117+
118+
/**
119+
* @param $targetId
120+
* @return Target|null
121+
*/
122+
public function getTarget($targetId)
123+
{
124+
// make sure target was created (via Target.targetCreated event)
125+
if (!array_key_exists($targetId, $this->targets)) {
126+
return null;
127+
}
128+
return $this->targets[$targetId];
129+
}
104130
}

src/Communication/Connection.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ class Connection extends EventEmitter implements LoggerAwareInterface
2222
{
2323

2424
const EVENT_TARGET_CREATED = 'method:Target.targetCreated';
25+
const EVENT_TARGET_DESTROYED = 'method:Target.targetDestroyed';
2526

2627
use LoggerAwareTrait;
2728

@@ -88,6 +89,14 @@ public function __construct($socketClient, LoggerInterface $logger = null)
8889
$this->wsClient = $socketClient;
8990
}
9091

92+
/**
93+
* @return LoggerInterface
94+
*/
95+
public function getLogger(): LoggerInterface
96+
{
97+
return $this->logger;
98+
}
99+
91100
/**
92101
* Set the delay to apply everytime before data are sent
93102
* @param $delay
@@ -226,6 +235,11 @@ public function createSession($targetId): Session
226235

227236
$this->sessions[$sessionId] = $session;
228237

238+
$session->on('destroyed', function () use ($sessionId) {
239+
$this->logger->debug('✘ session(' . $sessionId . ') was destroyed and unreferenced.');
240+
unset($this->sessions[$sessionId]);
241+
});
242+
229243
return $session;
230244
}
231245

src/Communication/Session.php

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use Evenement\EventEmitter;
99
use HeadlessChromium\Exception\CommunicationException;
1010
use HeadlessChromium\Exception\NoResponseAvailable;
11+
use HeadlessChromium\Exception\TargetDestroyed;
1112

1213
class Session extends EventEmitter
1314
{
@@ -27,6 +28,17 @@ class Session extends EventEmitter
2728
*/
2829
protected $connection;
2930

31+
/**
32+
* @var bool
33+
*/
34+
protected $destroyed = false;
35+
36+
/**
37+
* Session constructor.
38+
* @param string $targetId
39+
* @param string $sessionId
40+
* @param Connection $connection
41+
*/
3042
public function __construct(string $targetId, string $sessionId, Connection $connection)
3143
{
3244
$this->sessionId = $sessionId;
@@ -41,6 +53,10 @@ public function __construct(string $targetId, string $sessionId, Connection $con
4153
*/
4254
public function sendMessage(Message $message): SessionResponseReader
4355
{
56+
if ($this->destroyed) {
57+
throw new TargetDestroyed('The session was destroyed.');
58+
}
59+
4460
$topResponse = $this->connection->sendMessage(new Message('Target.sendMessageToTarget', [
4561
'message' => (string) $message,
4662
'sessionId' => $this->getSessionId()
@@ -89,6 +105,23 @@ public function getTargetId()
89105
*/
90106
public function getConnection()
91107
{
108+
if ($this->destroyed) {
109+
throw new TargetDestroyed('The session was destroyed.');
110+
}
92111
return $this->connection;
93112
}
113+
114+
/**
115+
* Marks the session as destroyed
116+
* @internal
117+
*/
118+
public function destroy()
119+
{
120+
if ($this->destroyed) {
121+
throw new TargetDestroyed('The session was already destroyed.');
122+
}
123+
$this->emit('destroyed');
124+
$this->connection = null;
125+
$this->removeAllListeners();
126+
}
94127
}

src/Communication/Target.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55

66
namespace HeadlessChromium\Communication;
77

8+
use HeadlessChromium\Exception\TargetDestroyed;
9+
810
class Target
911
{
1012
/**
@@ -17,6 +19,8 @@ class Target
1719
*/
1820
protected $session;
1921

22+
protected $destroyed = false;
23+
2024
/**
2125
* Target constructor.
2226
* @param array $targetInfo
@@ -33,6 +37,31 @@ public function __construct(array $targetInfo, Session $session)
3337
*/
3438
public function getSession(): Session
3539
{
40+
if ($this->destroyed) {
41+
throw new TargetDestroyed('The target was destroyed.');
42+
}
3643
return $this->session;
3744
}
45+
46+
/**
47+
* Marks the target as destroyed
48+
* @internal
49+
*/
50+
public function destroy()
51+
{
52+
if ($this->destroyed) {
53+
throw new TargetDestroyed('The target was already destroyed.');
54+
}
55+
$this->session->destroy();
56+
$this->session = null;
57+
$this->destroyed = true;
58+
}
59+
60+
/**
61+
* @return bool
62+
*/
63+
public function isDestroyed(): bool
64+
{
65+
return $this->destroyed;
66+
}
3867
}

src/Exception/TargetDestroyed.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
/**
3+
* @license see LICENSE
4+
*/
5+
6+
namespace HeadlessChromium\Exception;
7+
8+
class TargetDestroyed extends \RuntimeException
9+
{
10+
11+
}

src/Page.php

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use HeadlessChromium\Exception\CommunicationException;
1313
use HeadlessChromium\Exception\NoResponseAvailable;
1414
use HeadlessChromium\Exception\CommunicationException\ResponseHasError;
15+
use HeadlessChromium\Exception\TargetDestroyed;
1516
use HeadlessChromium\PageUtils\PageEvaluation;
1617
use HeadlessChromium\PageUtils\PageNavigation;
1718
use HeadlessChromium\PageUtils\PageScreenshot;
@@ -27,18 +28,35 @@ class Page
2728
*/
2829
protected $target;
2930

31+
/**
32+
* @var FrameManager
33+
*/
34+
protected $frameManager;
35+
3036
public function __construct(Target $target, array $frameTree)
3137
{
3238
$this->target = $target;
3339
$this->frameManager = new FrameManager($this, $frameTree);
3440
}
3541

42+
/**
43+
* @return FrameManager
44+
*/
45+
public function getFrameManager(): FrameManager
46+
{
47+
$this->assertNotClosed();
48+
49+
return $this->frameManager;
50+
}
51+
3652
/**
3753
* Get the session this page is attached to
3854
* @return Session
3955
*/
4056
public function getSession(): Session
4157
{
58+
$this->assertNotClosed();
59+
4260
return $this->target->getSession();
4361
}
4462

@@ -51,6 +69,8 @@ public function getSession(): Session
5169
*/
5270
public function navigate($url)
5371
{
72+
$this->assertNotClosed();
73+
5474
// make sure latest loaderId was pulled
5575
$this->getSession()->getConnection()->readData();
5676

@@ -88,6 +108,8 @@ public function navigate($url)
88108
*/
89109
public function evaluate(string $expression)
90110
{
111+
$this->assertNotClosed();
112+
91113
$currentLoaderId = $this->frameManager->getMainFrame()->getLatestLoaderId();
92114
$reader = $this->getSession()->sendMessage(
93115
new Message(
@@ -113,6 +135,8 @@ public function evaluate(string $expression)
113135
*/
114136
public function getCurrentLifecycle()
115137
{
138+
$this->assertNotClosed();
139+
116140
$this->getSession()->getConnection()->readData();
117141
return $this->frameManager->getMainFrame()->getLifeCycle();
118142
}
@@ -133,6 +157,8 @@ public function getCurrentLifecycle()
133157
*/
134158
public function hasLifecycleEvent(string $event): bool
135159
{
160+
$this->assertNotClosed();
161+
136162
return array_key_exists($event, $this->getCurrentLifecycle());
137163
}
138164

@@ -146,6 +172,8 @@ public function hasLifecycleEvent(string $event): bool
146172
*/
147173
public function waitForReload($eventName = Page::LOAD, $timeout = 30000, $loaderId = null)
148174
{
175+
$this->assertNotClosed();
176+
149177
if (!$loaderId) {
150178
$loaderId = $loader = $this->frameManager->getMainFrame()->getLatestLoaderId();
151179
}
@@ -197,6 +225,8 @@ private function waitForReloadGenerator($eventName, $loaderId)
197225
*/
198226
public function screenshot(array $options = []): PageScreenshot
199227
{
228+
$this->assertNotClosed();
229+
200230
$screenshotOptions = [];
201231

202232
// get format
@@ -265,4 +295,35 @@ public function screenshot(array $options = []): PageScreenshot
265295

266296
return new PageScreenshot($responseReader);
267297
}
298+
299+
/**
300+
* Request to close the page
301+
* @throws CommunicationException
302+
*/
303+
public function close()
304+
{
305+
306+
$this->assertNotClosed();
307+
308+
$this->getSession()
309+
->getConnection()
310+
->sendMessage(
311+
new Message(
312+
'Target.closeTarget',
313+
['targetId' => $this->getSession()->getTargetId()]
314+
)
315+
);
316+
317+
// TODO return close waiter
318+
}
319+
320+
/**
321+
* Throws if the page was closed
322+
*/
323+
private function assertNotClosed()
324+
{
325+
if ($this->target->isDestroyed()) {
326+
throw new TargetDestroyed('The page was closed and is not available anymore.');
327+
}
328+
}
268329
}

src/PageUtils/PageNavigation.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ class PageNavigation
4141
public function __construct(Page $page, string $previousLoaderId, string $currentLoaderId)
4242
{
4343
$this->page = $page;
44-
$this->frame = $this->page->frameManager->getMainFrame();
44+
$this->frame = $this->page->getFrameManager()->getMainFrame();
4545
$this->previousLoaderId = $previousLoaderId;
4646
$this->currentLoaderId = $currentLoaderId;
4747
}

0 commit comments

Comments
 (0)