1<?php
2
3namespace Facebook\WebDriver\Support\Events;
4
5use Facebook\WebDriver\Exception\UnsupportedOperationException;
6use Facebook\WebDriver\Exception\WebDriverException;
7use Facebook\WebDriver\Interactions\Touch\WebDriverTouchScreen;
8use Facebook\WebDriver\JavaScriptExecutor;
9use Facebook\WebDriver\WebDriver;
10use Facebook\WebDriver\WebDriverBy;
11use Facebook\WebDriver\WebDriverDispatcher;
12use Facebook\WebDriver\WebDriverElement;
13use Facebook\WebDriver\WebDriverOptions;
14use Facebook\WebDriver\WebDriverTargetLocator;
15use Facebook\WebDriver\WebDriverWait;
16
17class EventFiringWebDriver implements WebDriver, JavaScriptExecutor
18{
19    /**
20     * @var WebDriver
21     */
22    protected $driver;
23
24    /**
25     * @var WebDriverDispatcher
26     */
27    protected $dispatcher;
28
29    /**
30     * @param WebDriver $driver
31     * @param WebDriverDispatcher $dispatcher
32     */
33    public function __construct(WebDriver $driver, WebDriverDispatcher $dispatcher = null)
34    {
35        $this->dispatcher = $dispatcher ?: new WebDriverDispatcher();
36        if (!$this->dispatcher->getDefaultDriver()) {
37            $this->dispatcher->setDefaultDriver($this);
38        }
39        $this->driver = $driver;
40    }
41
42    /**
43     * @return WebDriverDispatcher
44     */
45    public function getDispatcher()
46    {
47        return $this->dispatcher;
48    }
49
50    /**
51     * @return WebDriver
52     */
53    public function getWebDriver()
54    {
55        return $this->driver;
56    }
57
58    /**
59     * @param mixed $url
60     * @throws WebDriverException
61     * @return $this
62     */
63    public function get($url)
64    {
65        $this->dispatch('beforeNavigateTo', $url, $this);
66
67        try {
68            $this->driver->get($url);
69        } catch (WebDriverException $exception) {
70            $this->dispatchOnException($exception);
71            throw $exception;
72        }
73        $this->dispatch('afterNavigateTo', $url, $this);
74
75        return $this;
76    }
77
78    /**
79     * @param WebDriverBy $by
80     * @throws WebDriverException
81     * @return array
82     */
83    public function findElements(WebDriverBy $by)
84    {
85        $this->dispatch('beforeFindBy', $by, null, $this);
86        $elements = [];
87
88        try {
89            foreach ($this->driver->findElements($by) as $element) {
90                $elements[] = $this->newElement($element);
91            }
92        } catch (WebDriverException $exception) {
93            $this->dispatchOnException($exception);
94            throw $exception;
95        }
96
97        $this->dispatch('afterFindBy', $by, null, $this);
98
99        return $elements;
100    }
101
102    /**
103     * @param WebDriverBy $by
104     * @throws WebDriverException
105     * @return EventFiringWebElement
106     */
107    public function findElement(WebDriverBy $by)
108    {
109        $this->dispatch('beforeFindBy', $by, null, $this);
110
111        try {
112            $element = $this->newElement($this->driver->findElement($by));
113        } catch (WebDriverException $exception) {
114            $this->dispatchOnException($exception);
115            throw $exception;
116        }
117
118        $this->dispatch('afterFindBy', $by, null, $this);
119
120        return $element;
121    }
122
123    /**
124     * @param string $script
125     * @param array $arguments
126     * @throws WebDriverException
127     * @return mixed
128     */
129    public function executeScript($script, array $arguments = [])
130    {
131        if (!$this->driver instanceof JavaScriptExecutor) {
132            throw new UnsupportedOperationException(
133                'driver does not implement JavaScriptExecutor'
134            );
135        }
136
137        $this->dispatch('beforeScript', $script, $this);
138
139        try {
140            $result = $this->driver->executeScript($script, $arguments);
141        } catch (WebDriverException $exception) {
142            $this->dispatchOnException($exception);
143            throw $exception;
144        }
145
146        $this->dispatch('afterScript', $script, $this);
147
148        return $result;
149    }
150
151    /**
152     * @param string $script
153     * @param array $arguments
154     * @throws WebDriverException
155     * @return mixed
156     */
157    public function executeAsyncScript($script, array $arguments = [])
158    {
159        if (!$this->driver instanceof JavaScriptExecutor) {
160            throw new UnsupportedOperationException(
161                'driver does not implement JavaScriptExecutor'
162            );
163        }
164
165        $this->dispatch('beforeScript', $script, $this);
166
167        try {
168            $result = $this->driver->executeAsyncScript($script, $arguments);
169        } catch (WebDriverException $exception) {
170            $this->dispatchOnException($exception);
171            throw $exception;
172        }
173        $this->dispatch('afterScript', $script, $this);
174
175        return $result;
176    }
177
178    /**
179     * @throws WebDriverException
180     * @return $this
181     */
182    public function close()
183    {
184        try {
185            $this->driver->close();
186
187            return $this;
188        } catch (WebDriverException $exception) {
189            $this->dispatchOnException($exception);
190            throw $exception;
191        }
192    }
193
194    /**
195     * @throws WebDriverException
196     * @return string
197     */
198    public function getCurrentURL()
199    {
200        try {
201            return $this->driver->getCurrentURL();
202        } catch (WebDriverException $exception) {
203            $this->dispatchOnException($exception);
204            throw $exception;
205        }
206    }
207
208    /**
209     * @throws WebDriverException
210     * @return string
211     */
212    public function getPageSource()
213    {
214        try {
215            return $this->driver->getPageSource();
216        } catch (WebDriverException $exception) {
217            $this->dispatchOnException($exception);
218            throw $exception;
219        }
220    }
221
222    /**
223     * @throws WebDriverException
224     * @return string
225     */
226    public function getTitle()
227    {
228        try {
229            return $this->driver->getTitle();
230        } catch (WebDriverException $exception) {
231            $this->dispatchOnException($exception);
232            throw $exception;
233        }
234    }
235
236    /**
237     * @throws WebDriverException
238     * @return string
239     */
240    public function getWindowHandle()
241    {
242        try {
243            return $this->driver->getWindowHandle();
244        } catch (WebDriverException $exception) {
245            $this->dispatchOnException($exception);
246            throw $exception;
247        }
248    }
249
250    /**
251     * @throws WebDriverException
252     * @return array
253     */
254    public function getWindowHandles()
255    {
256        try {
257            return $this->driver->getWindowHandles();
258        } catch (WebDriverException $exception) {
259            $this->dispatchOnException($exception);
260            throw $exception;
261        }
262    }
263
264    /**
265     * @throws WebDriverException
266     */
267    public function quit()
268    {
269        try {
270            $this->driver->quit();
271        } catch (WebDriverException $exception) {
272            $this->dispatchOnException($exception);
273            throw $exception;
274        }
275    }
276
277    /**
278     * @param null|string $save_as
279     * @throws WebDriverException
280     * @return string
281     */
282    public function takeScreenshot($save_as = null)
283    {
284        try {
285            return $this->driver->takeScreenshot($save_as);
286        } catch (WebDriverException $exception) {
287            $this->dispatchOnException($exception);
288            throw $exception;
289        }
290    }
291
292    /**
293     * @param int $timeout_in_second
294     * @param int $interval_in_millisecond
295     * @throws WebDriverException
296     * @return WebDriverWait
297     */
298    public function wait($timeout_in_second = 30, $interval_in_millisecond = 250)
299    {
300        try {
301            return $this->driver->wait($timeout_in_second, $interval_in_millisecond);
302        } catch (WebDriverException $exception) {
303            $this->dispatchOnException($exception);
304            throw $exception;
305        }
306    }
307
308    /**
309     * @throws WebDriverException
310     * @return WebDriverOptions
311     */
312    public function manage()
313    {
314        try {
315            return $this->driver->manage();
316        } catch (WebDriverException $exception) {
317            $this->dispatchOnException($exception);
318            throw $exception;
319        }
320    }
321
322    /**
323     * @throws WebDriverException
324     * @return EventFiringWebDriverNavigation
325     */
326    public function navigate()
327    {
328        try {
329            return new EventFiringWebDriverNavigation(
330                $this->driver->navigate(),
331                $this->getDispatcher()
332            );
333        } catch (WebDriverException $exception) {
334            $this->dispatchOnException($exception);
335            throw $exception;
336        }
337    }
338
339    /**
340     * @throws WebDriverException
341     * @return WebDriverTargetLocator
342     */
343    public function switchTo()
344    {
345        try {
346            return $this->driver->switchTo();
347        } catch (WebDriverException $exception) {
348            $this->dispatchOnException($exception);
349            throw $exception;
350        }
351    }
352
353    /**
354     * @throws WebDriverException
355     * @return WebDriverTouchScreen
356     */
357    public function getTouch()
358    {
359        try {
360            return $this->driver->getTouch();
361        } catch (WebDriverException $exception) {
362            $this->dispatchOnException($exception);
363            throw $exception;
364        }
365    }
366
367    public function execute($name, $params)
368    {
369        try {
370            return $this->driver->execute($name, $params);
371        } catch (WebDriverException $exception) {
372            $this->dispatchOnException($exception);
373            throw $exception;
374        }
375    }
376
377    /**
378     * @param WebDriverElement $element
379     * @return EventFiringWebElement
380     */
381    protected function newElement(WebDriverElement $element)
382    {
383        return new EventFiringWebElement($element, $this->getDispatcher());
384    }
385
386    /**
387     * @param mixed $method
388     * @param mixed ...$arguments
389     */
390    protected function dispatch($method, ...$arguments)
391    {
392        if (!$this->dispatcher) {
393            return;
394        }
395
396        $this->dispatcher->dispatch($method, $arguments);
397    }
398
399    /**
400     * @param WebDriverException $exception
401     */
402    protected function dispatchOnException(WebDriverException $exception)
403    {
404        $this->dispatch('onException', $exception, $this);
405    }
406}
407