Skip to content

Commit 96228f8

Browse files
committed
reviews for #34
1 parent c450ac2 commit 96228f8

File tree

10 files changed

+157
-35
lines changed

10 files changed

+157
-35
lines changed

CHANGELOG.md

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,19 @@
1111
* Bug fixes:
1212
* none
1313

14+
## x.x.x
15+
16+
> *20xx-xx-xx* (not released)
17+
18+
> Description
19+
20+
* Features:
21+
* Added method ``Browser::setPagePreScript``
22+
* Added method ``Page::addPreScript``
23+
* Added option ``"nosandbox"`` for browser factory
24+
* Added option ``"sendSyncDefaultTimeout"`` for browser factory
25+
* Bug fixes:
26+
* Fixed user agent string for browser factory
1427

1528
## 0.2.2
1629

@@ -22,10 +35,9 @@
2235
* Added a shortcut to get current page url: ``Page::getCurrentUrl``
2336
* Added ability to get and set cookies from a page: ``Page.setCookies``, ``Page.readCookies`` , ``Page.readAllCookies``
2437
* improved some error reporting
25-
* fixed a bug with directory creation for screenshots
2638
* added ability to set custom user agent: ``Page::setUserAgent`` or via factory option ``userAgent``
2739
* Bug fixes:
28-
* none
40+
* fixed a bug with directory creation for screenshots
2941

3042
## 0.2.1
3143

README.md

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,13 +130,16 @@ Here are the options available for the browser factory:
130130
| Option name | Default | Description |
131131
|--------------------|-----------------------|-----------------------------------------------------------------------------------|
132132
| connectionDelay | 0 | Delay to apply between each operation for debugging purposes |
133+
| debug | false | Allows to enable debug mode |
133134
| debugLogger | null | A string (e.g "php://stdout"), or resource, or PSR-3 logger instance to print debug messages |
134135
| enableImages | true | Toggles loading of images |
135136
| headless | true | Enable or disable headless mode |
137+
| noSandbox | false | Useful to run in a docker container |
138+
| sendSyncDefaultTimeout | 3000 | Default timeout (ms) for sending sync messages |
139+
| startupTimeout | 30 | Maximum time in seconds to wait for chrome to start |
136140
| userAgent | none | User agent to use for the whole browser (see page api for alternative) |
137141
| userDataDir | none | chrome user data dir (default: a new empty dir is generated temporarily) |
138-
| startupTimeout | 30 | Maximum time in seconds to wait for chrome to start |
139-
| windowSize | - | Size of the window. usage: ``[$width, $height]`` - see also Page::setViewportSize |
142+
| windowSize | none | Size of the window. usage: ``[$width, $height]`` - see also Page::setViewportSize |
140143

141144
### Browser API
142145

@@ -156,6 +159,21 @@ Here are the options available for the browser factory:
156159
$browser->close();
157160
```
158161

162+
### Set a script to evaluate before every page created by this browser will navigate
163+
164+
```php
165+
$script =
166+
'// Simulate navigator permissions;
167+
const originalQuery = window.navigator.permissions.query;
168+
window.navigator.permissions.query = (parameters) => (
169+
parameters.name === 'notifications' ?
170+
Promise.resolve({ state: Notification.permission }) :
171+
originalQuery(parameters)
172+
);'
173+
174+
$browser->setPagePreScript($script);
175+
```
176+
159177
### Page API
160178

161179
#### Navigate to an url
@@ -223,6 +241,20 @@ will want to wait for the new page to reload.
223241
You can achieve this by using ``$page->evaluate('some js that will reload the page')->waitForPageReload()``.
224242
An example is available in [form-submit.php](./examples/form-submit.php)
225243

244+
### Add a script to evaluate upon page navigation
245+
246+
```php
247+
$script =
248+
'// Simulate navigator permissions;
249+
const originalQuery = window.navigator.permissions.query;
250+
window.navigator.permissions.query = (parameters) => (
251+
parameters.name === 'notifications' ?
252+
Promise.resolve({ state: Notification.permission }) :
253+
originalQuery(parameters)
254+
);'
255+
256+
$page->addPreScript($script);
257+
```
226258

227259
#### Set viewport size
228260

src/Browser.php

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use HeadlessChromium\Exception\CommunicationException;
1212
use HeadlessChromium\Exception\NoResponseAvailable;
1313
use HeadlessChromium\Exception\CommunicationException\ResponseHasError;
14+
use HeadlessChromium\Exception\OperationTimedOut;
1415

1516
class Browser
1617
{
@@ -24,6 +25,12 @@ class Browser
2425
*/
2526
protected $targets = [];
2627

28+
/**
29+
* A preScript to be automatically added on every new pages
30+
* @var string|null
31+
*/
32+
protected $pagePreScript;
33+
2734
public function __construct(Connection $connection)
2835
{
2936
$this->connection = $connection;
@@ -77,6 +84,17 @@ public function getConnection(): Connection
7784
return $this->connection;
7885
}
7986

87+
/**
88+
* Set a preScript to be added on every new pages.
89+
* Use null to disable it.
90+
*
91+
* @param string|null $script
92+
*/
93+
public function setPagePreScript(string $script = null)
94+
{
95+
$this->pagePreScript = $script;
96+
}
97+
8098
/**
8199
* Closes the browser
82100
*/
@@ -90,9 +108,10 @@ public function close()
90108
* Creates a new page
91109
* @throws NoResponseAvailable
92110
* @throws CommunicationException
111+
* @throws OperationTimedOut
93112
* @return Page
94113
*/
95-
public function createPage(array $options = []): Page
114+
public function createPage(): Page
96115
{
97116

98117
// page url
@@ -118,7 +137,7 @@ public function createPage(array $options = []): Page
118137
}
119138

120139
// create page
121-
$page = new Page($target, $frameTreeResponse['result']['frameTree'], $options);
140+
$page = new Page($target, $frameTreeResponse['result']['frameTree']);
122141

123142
// Page.enable
124143
$page->getSession()->sendMessageSync(new Message('Page.enable'));
@@ -129,6 +148,11 @@ public function createPage(array $options = []): Page
129148
// Page.setLifecycleEventsEnabled
130149
$page->getSession()->sendMessageSync(new Message('Page.setLifecycleEventsEnabled', ['enabled' => true]));
131150

151+
// add prescript
152+
if ($this->pagePreScript) {
153+
$page->addPreScript($this->pagePreScript);
154+
}
155+
132156
return $page;
133157
}
134158

src/Browser/BrowserProcess.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -319,10 +319,11 @@ private function getArgsFromOptions(array $options)
319319
}
320320

321321
// sandbox mode - useful if you want to use chrome headless inside docker
322-
if (array_key_exists('nosandbox', $options)) {
322+
if (array_key_exists('noSandbox', $options) && $options['noSandbox']) {
323323
$args[] = '--no-sandbox';
324324
}
325325

326+
// user agent
326327
if (array_key_exists('userAgent', $options)) {
327328
$args[] = '--user-agent=' . escapeshellarg($options['userAgent']);
328329
}

src/BrowserFactory.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,13 @@ public function __construct(string $chromeBinaries = null)
3535
*
3636
* @param array $options options for browser creation:
3737
* - connectionDelay: amount of time in seconds to slows down connection for debugging purposes (default: none)
38-
* - debug: toggles the debug mode than allows to print additional details (default: false)
38+
* - debug: toggles the debug mode that allows to print additional details (default: false)
3939
* - debugLogger: resource string ("php://stdout"), resource or psr-3 logger instance (default: none)
4040
* enabling debug logger will also enable debug mode.
4141
* - enableImages: toggle the loading of images (default: true)
4242
* - headless: whether chrome should be started headless (default: true)
43+
* - noSandbox: enable no sandbox mode (default: false)
44+
* - sendSyncDefaultTimeout: maximum time in ms to wait for synchronous messages to send (default 3000 ms)
4345
* - startupTimeout: maximum time in seconds to wait for chrome to start (default: 30 sec)
4446
* - userAgent: user agent to use for the browser
4547
* - userDataDir: chrome user data dir (default: a new empty dir is generated temporarily)

src/Communication/Connection.php

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ class Connection extends EventEmitter implements LoggerAwareInterface
6262
* Default timeout for send sync in ms
6363
* @var int
6464
*/
65-
protected $sendSyncDefaultTimeout = 3000;
65+
protected $sendSyncDefaultTimeout;
6666

6767
/**
6868
* @var Session[]
@@ -77,13 +77,15 @@ class Connection extends EventEmitter implements LoggerAwareInterface
7777
/**
7878
* CommunicationChannel constructor.
7979
* @param SocketInterface|string $socketClient
80+
* @param int|null $sendSyncDefaultTimeout
8081
*/
81-
public function __construct($socketClient, LoggerInterface $logger = null, ?int $sendSyncDefaultTimeout = 3000)
82+
public function __construct($socketClient, LoggerInterface $logger = null, int $sendSyncDefaultTimeout = null)
8283
{
8384
// set or create logger
8485
$this->setLogger($logger ?? new NullLogger());
8586

86-
$this->sendSyncDefaultTimeout = $sendSyncDefaultTimeout;
87+
// set timeout
88+
$this->sendSyncDefaultTimeout = $sendSyncDefaultTimeout ?? 3000;
8789

8890
// create socket client
8991
if (is_string($socketClient)) {
@@ -220,7 +222,7 @@ public function sendMessage(Message $message): ResponseReader
220222
* @throws OperationTimedOut
221223
* @return Response
222224
*/
223-
public function sendMessageSync(Message $message, $timeout = null): Response
225+
public function sendMessageSync(Message $message, int $timeout = null): Response
224226
{
225227
$responseReader = $this->sendMessage($message);
226228
$response = $responseReader->waitForResponse($timeout ?? $this->sendSyncDefaultTimeout);

src/Page.php

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -43,15 +43,28 @@ class Page
4343
protected $mouse;
4444

4545
/**
46-
* @var array
46+
* Page constructor.
47+
* @param Target $target
48+
* @param array $frameTree
4749
*/
48-
protected $options = [];
49-
50-
public function __construct(Target $target, array $frameTree, array $options = [])
50+
public function __construct(Target $target, array $frameTree)
5151
{
5252
$this->target = $target;
5353
$this->frameManager = new FrameManager($this, $frameTree);
54-
$this->options = $options;
54+
}
55+
56+
/**
57+
* Adds a script to be evaluated upon page navigation
58+
*
59+
* @param string $script
60+
* @throws CommunicationException
61+
* @throws NoResponseAvailable
62+
*/
63+
public function addPreScript(string $script)
64+
{
65+
$this->getSession()->sendMessageSync(
66+
new Message('Page.addScriptToEvaluateOnNewDocument', ['source' => $script])
67+
);
5568
}
5669

5770
/**
@@ -79,15 +92,11 @@ public function getSession(): Session
7992
* @param $url
8093
* @return PageNavigation
8194
*/
82-
public function navigate($url, ?string $preScript = NULL)
95+
public function navigate($url)
8396
{
8497
$this->assertNotClosed();
8598

86-
if (isset($this->options['preScript'])){
87-
$preScript = $this->options['preScript'].$preScript;
88-
}
89-
90-
return new PageNavigation($this, $url, $preScript);
99+
return new PageNavigation($this, $url);
91100
}
92101

93102
/**

src/PageUtils/PageNavigation.php

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,9 @@ class PageNavigation
5757
* @throws Exception\CommunicationException
5858
* @throws Exception\CommunicationException\CannotReadResponse
5959
* @throws Exception\CommunicationException\InvalidResponse
60+
* @throws Exception\NoResponseAvailable
6061
*/
61-
public function __construct(Page $page, string $url, ?string $preScript = NULL)
62+
public function __construct(Page $page, string $url)
6263
{
6364

6465
// make sure latest loaderId was pulled
@@ -67,13 +68,6 @@ public function __construct(Page $page, string $url, ?string $preScript = NULL)
6768
// get previous loaderId for the navigation watcher
6869
$this->previousLoaderId = $page->getFrameManager()->getMainFrame()->getLatestLoaderId();
6970

70-
71-
if ($preScript !== NULL){
72-
$page->getSession()->sendMessageSync(
73-
new Message('Page.addScriptToEvaluateOnNewDocument', ['source'=>$preScript])
74-
);
75-
}
76-
7771
// send navigation message
7872
$this->navigateResponseReader = $page->getSession()->sendMessage(
7973
new Message('Page.navigate', ['url' => $url])

test/suites/BrowserFactoryTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,13 @@ public function testUserAgentOption()
3434
$factory = new BrowserFactory();
3535

3636
$browser = $factory->createBrowser([
37-
'userAgent' => 'foobarbaz'
37+
'userAgent' => 'foo bar baz'
3838
]);
3939

4040
$page = $browser->createPage();
4141

4242
$response = $page->evaluate('navigator.userAgent')->getReturnValue();
4343

44-
$this->assertEquals('foobarbaz', $response);
44+
$this->assertEquals('foo bar baz', $response);
4545
}
4646
}

test/suites/PageTest.php

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,13 +57,59 @@ public function testSetUserAgent()
5757
$pageFooBar->setUserAgent('foobar')->await();
5858
$pageBarBaz->setUserAgent('barbaz')->await();
5959

60-
$pageFooBar->navigate('http://requestbin.fullcontact.com/uhunfhuh')->waitForNavigation();
61-
$pageBarBaz->navigate('http://requestbin.fullcontact.com/uhunfhuh')->waitForNavigation();
60+
$pageFooBar->navigate($this->sitePath('a.html'))->waitForNavigation();
61+
$pageBarBaz->navigate($this->sitePath('a.html'))->waitForNavigation();
6262

6363
$value1 = $pageFooBar->evaluate('navigator.userAgent')->getReturnValue();
6464
$value2 = $pageBarBaz->evaluate('navigator.userAgent')->getReturnValue();
6565

6666
$this->assertEquals('foobar', $value1);
6767
$this->assertEquals('barbaz', $value2);
6868
}
69+
70+
71+
public function testPreScriptOption()
72+
{
73+
$factory = new BrowserFactory();
74+
75+
$browser = $factory->createBrowser();
76+
77+
$preScript1 =
78+
"if(!('foo' in navigator)) {
79+
navigator.foo = 0
80+
}
81+
navigator.foo++;";
82+
83+
$preScript2 =
84+
"if(!('bar' in navigator)) {
85+
navigator.bar = 10
86+
}
87+
navigator.bar++;";
88+
89+
$page = $browser->createPage();
90+
$page2 = $browser->createPage();
91+
$page->addPreScript($preScript1);
92+
$page->addPreScript($preScript2);
93+
94+
// make sure prescript evaluates
95+
$page->navigate($this->sitePath('a.html'))->waitForNavigation();
96+
$fooValue = $page->evaluate('navigator.foo')->getReturnValue();
97+
$barValue = $page->evaluate('navigator.bar')->getReturnValue();
98+
$this->assertEquals(1, $fooValue);
99+
$this->assertEquals(11, $barValue);
100+
101+
// make sure prescript is not adding again and again on every requests
102+
$page->navigate($this->sitePath('b.html'))->waitForNavigation();
103+
$fooValue = $page->evaluate('navigator.foo')->getReturnValue();
104+
$barValue = $page->evaluate('navigator.bar')->getReturnValue();
105+
$this->assertEquals(1, $fooValue);
106+
$this->assertEquals(11, $barValue);
107+
108+
// make sure prescript did not pollute other pages
109+
$page2->navigate($this->sitePath('b.html'))->waitForNavigation();
110+
$fooValue = $page2->evaluate('navigator.foo')->getReturnValue();
111+
$barValue = $page2->evaluate('navigator.bar')->getReturnValue();
112+
$this->assertEquals(null, $fooValue);
113+
$this->assertEquals(null, $barValue);
114+
}
69115
}

0 commit comments

Comments
 (0)