Skip to content

Commit 17479dd

Browse files
authored
Fix scroll method returning prematurely (#240)
1 parent 2c8b17e commit 17479dd

File tree

2 files changed

+72
-8
lines changed

2 files changed

+72
-8
lines changed

src/Input/Mouse.php

Lines changed: 72 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use HeadlessChromium\Communication\Message;
1515
use HeadlessChromium\Page;
16+
use HeadlessChromium\Utils;
1617

1718
class Mouse
1819
{
@@ -162,17 +163,28 @@ public function scrollDown(int $distance)
162163
/**
163164
* Scroll a positive or negative distance using the mouseWheel event type.
164165
*
165-
* @param int $distance Distance in pixels
166+
* @param int $distanceY Distance in pixels for the Y axis
167+
* @param int $distanceX (optional) Distance in pixels for the X axis
166168
*
167169
* @throws \HeadlessChromium\Exception\CommunicationException
168170
* @throws \HeadlessChromium\Exception\NoResponseAvailable
171+
* @throws \HeadlessChromium\Exception\OperationTimedOut
169172
*
170173
* @return $this
171174
*/
172-
private function scroll(int $distance)
175+
private function scroll(int $distanceY, int $distanceX = 0): self
173176
{
174177
$this->page->assertNotClosed();
175178

179+
$scollableArea = $this->page->getLayoutMetrics()->getContentSize();
180+
$visibleArea = $this->page->getLayoutMetrics()->getVisualViewport();
181+
182+
$distanceX = $this->getMaximumDistance($distanceX, $visibleArea['pageX'], $scollableArea['height']);
183+
$distanceY = $this->getMaximumDistance($distanceY, $visibleArea['pageY'], $scollableArea['width']);
184+
185+
$targetX = $visibleArea['pageX'] + $distanceX;
186+
$targetY = $visibleArea['pageY'] + $distanceY;
187+
176188
// make sure the mouse is on the screen
177189
$this->move($this->x, $this->y);
178190

@@ -181,13 +193,68 @@ private function scroll(int $distance)
181193
'type' => 'mouseWheel',
182194
'x' => $this->x,
183195
'y' => $this->y,
184-
'deltaX' => 0,
185-
'deltaY' => $distance,
196+
'deltaX' => $distanceX,
197+
'deltaY' => $distanceY,
186198
]));
187199

200+
// wait until the scroll is done
201+
Utils::tryWithTimeout(30000 * 1000, $this->waitForScroll($targetX, $targetY));
202+
188203
// set new position after move
189-
$this->y += $distance;
204+
$this->x += $distanceX;
205+
$this->y += $distanceY;
190206

191207
return $this;
192208
}
209+
210+
/**
211+
* Get the maximum distance to scroll a page.
212+
*
213+
* @param int $distance Distance to scroll, positive or negative
214+
* @param int $current Current position
215+
* @param int $maximum Maximum posible distance
216+
*
217+
* @return int allowed distance to scroll
218+
*/
219+
private function getMaximumDistance(int $distance, int $current, int $maximum): int
220+
{
221+
$result = $current + $distance;
222+
223+
if ($result < 0) {
224+
return $distance + \abs($result);
225+
}
226+
227+
if ($result > $maximum) {
228+
return $maximum - $current;
229+
}
230+
231+
return $distance;
232+
}
233+
234+
/**
235+
* Wait for the browser to process the scroll command.
236+
*
237+
* Return the number of microseconds to wait before trying again or true in case of success.
238+
*
239+
* @see \HeadlessChromium\Utils::tryWithTimeout
240+
*
241+
* @param int $targetX
242+
* @param int $targetY
243+
*
244+
* @throws \HeadlessChromium\Exception\OperationTimedOut
245+
*
246+
* @return bool|\Generator
247+
*/
248+
private function waitForScroll(int $targetX, int $targetY)
249+
{
250+
while (true) {
251+
$visibleArea = $this->page->getLayoutMetrics()->getVisualViewport();
252+
253+
if ($visibleArea['pageX'] === $targetX && $visibleArea['pageY'] === $targetY) {
254+
return true;
255+
}
256+
257+
yield 1000;
258+
}
259+
}
193260
}

tests/MouseApiTest.php

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,19 +74,16 @@ public function testScroll(): void
7474
{
7575
// initial navigation
7676
$page = $this->openSitePage('bigLayout.html');
77-
\usleep(200000);
7877

7978
// scroll 100px down
8079
$page->mouse()->scrollDown(100);
81-
\usleep(200000);
8280

8381
$windowScrollY = $page->evaluate('window.scrollY')->getReturnValue();
8482

8583
$this->assertEquals(100, $windowScrollY);
8684

8785
// scrolling 100px up should revert the last action
8886
$page->mouse()->scrollUp(100);
89-
\usleep(200000);
9087

9188
$windowScrollY = $page->evaluate('window.scrollY')->getReturnValue();
9289

0 commit comments

Comments
 (0)