com.seleniumtests.driver.CustomEventFiringWebDriver.java Source code

Java tutorial

Introduction

Here is the source code for com.seleniumtests.driver.CustomEventFiringWebDriver.java

Source

/**
 * Orignal work: Copyright 2015 www.seleniumtests.com
 * Modified work: Copyright 2016 www.infotel.com
 *             Copyright 2017-2019 B.Hecquet
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.seleniumtests.driver;

import java.awt.AWTException;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.HeadlessException;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.datatransfer.StringSelection;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.UUID;

import javax.imageio.ImageIO;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Base64OutputStream;
import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;
import org.openqa.selenium.Alert;
import org.openqa.selenium.Capabilities;
import org.openqa.selenium.Dimension;
import org.openqa.selenium.HasCapabilities;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.MutableCapabilities;
import org.openqa.selenium.Point;
import org.openqa.selenium.UnhandledAlertException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebDriverException;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.remote.FileDetector;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.openqa.selenium.remote.UselessFileDetector;
import org.openqa.selenium.support.events.EventFiringWebDriver;

import com.neotys.selenium.proxies.NLWebDriver;
import com.seleniumtests.browserfactory.BrowserInfo;
import com.seleniumtests.connectors.selenium.SeleniumGridConnector;
import com.seleniumtests.customexception.DriverExceptions;
import com.seleniumtests.customexception.ScenarioException;
import com.seleniumtests.driver.screenshots.VideoRecorder;
import com.seleniumtests.util.helper.WaitHelper;
import com.seleniumtests.util.logging.SeleniumRobotLogger;
import com.seleniumtests.util.osutility.OSUtilityFactory;

import net.lightbody.bmp.BrowserMobProxy;

/**
 * This class acts as a proxy for everything related to selenium driver actions (mostly a bypass) or with the machine holding
 * the browser
 * - send keys via keyboard
 * - move mouse
 * - upload file to browser
 * - capture video
 * 
 * When action do not need a real driver, static methods are provided
 * It also handles the grid mode, masking it to requester.
 */
public class CustomEventFiringWebDriver extends EventFiringWebDriver implements HasCapabilities {

    private static final Logger logger = SeleniumRobotLogger.getLogger(CustomEventFiringWebDriver.class);
    private FileDetector fileDetector = new UselessFileDetector();
    private Set<String> currentHandles;
    private final List<Long> driverPids;
    private final WebDriver driver;
    private final NLWebDriver neoloadDriver;
    private final boolean isWebTest;
    private final DriverMode driverMode;
    private final BrowserInfo browserInfo;
    private final BrowserMobProxy mobProxy;
    private final SeleniumGridConnector gridConnector;

    private static final String JS_GET_VIEWPORT_SIZE = "var pixelRatio;"
            + "try{pixelRatio = devicePixelRatio} catch(err){pixelRatio=1}" + "var height = 100000;"
            + "var width = 100000;" + " if (window.innerHeight) {"
            + "      height = Math.min(window.innerHeight, height);" + " }"
            + " if (document.documentElement && document.documentElement.clientHeight) {"
            + "      height = Math.min(document.documentElement.clientHeight, height);" + " }"
            + "   var b = document.getElementsByTagName('html')[0]; " + "   if (b.clientHeight) {"
            + "      height = Math.min(b.clientHeight, height);" + "   }" + " if (window.innerWidth) {"
            + "      width = Math.min(window.innerWidth, width);" + " } "
            + " if (document.documentElement && document.documentElement.clientWidth) {"
            + "      width = Math.min(document.documentElement.clientWidth, width);" + " } "
            + "   var b = document.getElementsByTagName('html')[0]; " + "   if (b.clientWidth) {"
            + "      width = Math.min(b.clientWidth, width);" + "   }"
            + "   return [width * pixelRatio, height * pixelRatio];";

    private static final String JS_GET_CURRENT_SCROLL_POSITION = "var doc = document.documentElement; "
            + "var x = window.scrollX || ((window.pageXOffset || doc.scrollLeft) - (doc.clientLeft || 0));"
            + "var y = window.scrollY || ((window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0));"
            + "return [x, y];";

    // IMPORTANT: Notice there's a major difference between scrollWidth
    // and scrollHeight. While scrollWidth is the maximum between an
    // element's width and its content width, scrollHeight might be
    // smaller (!) than the clientHeight, which is why we take the
    // maximum between them.
    private static final String JS_GET_CONTENT_ENTIRE_SIZE = "var pixelRatio;"
            + "try{pixelRatio = devicePixelRatio} catch(err){pixelRatio=1}"
            + "var scrollWidth = document.documentElement.scrollWidth; "
            + "var bodyScrollWidth = document.body.scrollWidth; "
            + "var totalWidth = Math.max(scrollWidth, bodyScrollWidth); "
            + "var clientHeight = document.documentElement.clientHeight; "
            + "var bodyClientHeight = document.body.clientHeight; "
            + "var scrollHeight = document.documentElement.scrollHeight; "
            + "var bodyScrollHeight = document.body.scrollHeight; "
            + "var maxDocElementHeight = Math.max(clientHeight, scrollHeight); "
            + "var maxBodyHeight = Math.max(bodyClientHeight, bodyScrollHeight); "
            + "var totalHeight = Math.max(maxDocElementHeight, maxBodyHeight); "
            + "return [totalWidth * pixelRatio, totalHeight * pixelRatio];";

    public static final String NON_JS_UPLOAD_FILE_THROUGH_POPUP = "var action = 'upload_file_through_popup';";
    public static final String NON_JS_CAPTURE_DESKTOP = "var action = 'capture_desktop_snapshot_to_base64_string';return '';";

    public CustomEventFiringWebDriver(final WebDriver driver) {
        this(driver, null, null, true, DriverMode.LOCAL, null, null);
    }

    public CustomEventFiringWebDriver(final WebDriver driver, List<Long> driverPids, BrowserInfo browserInfo,
            Boolean isWebTest, DriverMode localDriver, BrowserMobProxy mobProxy,
            SeleniumGridConnector gridConnector) {
        super(driver);
        this.driverPids = driverPids == null ? new ArrayList<>() : driverPids;
        this.driver = driver;
        this.browserInfo = browserInfo;
        this.isWebTest = isWebTest;
        this.driverMode = localDriver;
        this.mobProxy = mobProxy;
        this.gridConnector = gridConnector;

        // NEOLOAD //
        if (driver instanceof NLWebDriver) {
            neoloadDriver = (NLWebDriver) driver;
        } else {
            neoloadDriver = null;
        }
    }

    public void setFileDetector(final FileDetector detector) {
        if (detector == null) {
            throw new WebDriverException("file detector is null");
        }

        fileDetector = detector;
    }

    /**
     * Method for updating window handles when an operation may create a new window (a click action)
     * This is called for Composite actions, native actions (from DriverListener) and JS actions
     */
    public void updateWindowsHandles() {
        if (isWebTest) {
            currentHandles = getWindowHandles();

        } else {
            currentHandles = new TreeSet<>();
        }
    }

    @Override
    public Set<String> getWindowHandles() {

        if (!isWebTest) {
            return new TreeSet<>();
        }

        // issue #169: workaround for ios / IE tests where getWindowHandles sometimes fails with: class org.openqa.selenium.WebDriverException: Returned value cannot be converted to List<String>: true
        for (int i = 0; i < 10; i++) {
            try {
                return super.getWindowHandles();
            } catch (UnhandledAlertException e) {
                logger.info("getWindowHandles: Handling alert");
                handleAlert();
                return super.getWindowHandles();
            } catch (Exception e) {
                logger.info("error getting window handles: " + e.getMessage());
                WaitHelper.waitForSeconds(2);
            }
        }
        return super.getWindowHandles();

    }

    private void handleAlert() {
        try {
            Alert alert = driver.switchTo().alert();
            alert.dismiss();
        } catch (Exception e) {
        }
    }

    @Override
    public String getWindowHandle() {

        if (!isWebTest) {
            return "";
        }

        try {
            return super.getWindowHandle();
        } catch (UnhandledAlertException e) {
            logger.info("getWindowHandle: Handling alert");
            handleAlert();
            return super.getWindowHandle();
        }
    }

    @Override
    public void close() {
        try {
            super.close();
        } catch (UnhandledAlertException e) {
            logger.info("close: Handling alert");
            handleAlert();
            super.close();
        }
    }

    @Override
    public String getCurrentUrl() {
        try {
            return super.getCurrentUrl();
        } catch (UnhandledAlertException e) {
            logger.info("getCurrentUrl: Handling alert");
            handleAlert();
            return super.getCurrentUrl();
        }
    }

    @Override
    public String getTitle() {
        try {
            return super.getTitle();
        } catch (UnhandledAlertException e) {
            logger.info("getTitle: Handling alert");
            handleAlert();
            return super.getTitle();
        }
    }

    public FileDetector getFileDetector() {
        return fileDetector;
    }

    public WebDriver getWebDriver() {
        return driver;
    }

    public String getSessionId() {
        try {
            return ((RemoteWebDriver) driver).getSessionId().toString();
        } catch (ClassCastException e) {
            return UUID.randomUUID().toString();
        }
    }

    /**
     * Handle WebDriver exception when this method is not implemented
     */
    @Override
    public String getPageSource() {
        try {
            return super.getPageSource();
        } catch (UnhandledAlertException e) {
            logger.info("getPageSource: Handling alert");
            handleAlert();
            return super.getPageSource();
        } catch (WebDriverException e) {
            logger.info("page source not get: " + e.getMessage());
            return null;
        }
    }

    public Set<String> getCurrentHandles() {
        return currentHandles;
    }

    /**
     * get dimensions of the visible part of the page
     * TODO: handle mobile app case
     * @return
     */
    @SuppressWarnings("unchecked")
    public Dimension getViewPortDimensionWithoutScrollbar() {
        if (isWebTest) {
            try {
                List<Number> dims = (List<Number>) ((JavascriptExecutor) driver)
                        .executeScript(JS_GET_VIEWPORT_SIZE);
                return new Dimension(dims.get(0).intValue(), dims.get(1).intValue());
            } catch (Exception e) {
                return driver.manage().window().getSize();
            }
        } else {
            return driver.manage().window().getSize();
        }
    }

    /**
     * Get the whole webpage dimension
     * TODO: handle mobile app case
     * @return
     */

    @SuppressWarnings("unchecked")
    public Dimension getContentDimension() {
        if (isWebTest) {
            try {
                List<Number> dims = (List<Number>) ((JavascriptExecutor) driver)
                        .executeScript(JS_GET_CONTENT_ENTIRE_SIZE);
                return new Dimension(dims.get(0).intValue(), dims.get(1).intValue());
            } catch (Exception e) {
                return driver.manage().window().getSize();
            }
        } else {
            return driver.manage().window().getSize();
        }
    }

    /**
     * TODO: handle mobile app case
     */
    public void scrollTop() {
        if (isWebTest) {
            ((JavascriptExecutor) driver).executeScript("window.top.scroll(0, 0)");
        }
    }

    public void scrollTo(int x, int y) {
        if (isWebTest) {
            ((JavascriptExecutor) driver).executeScript(String.format("window.top.scroll(%d, %d)", x, y));

            // wait for scrolling end
            Point previousScrollPosition = getScrollPosition();
            int i = 0;
            boolean fixed = false;
            do {
                Point scrollPosition = getScrollPosition();
                if (scrollPosition.x == x && scrollPosition.y == y) {
                    break;
                } else if (scrollPosition.x == previousScrollPosition.x
                        && scrollPosition.y == previousScrollPosition.y) {
                    if (!fixed) {
                        fixed = true;
                    } else {
                        break;
                    }
                }
                previousScrollPosition = scrollPosition;
                WaitHelper.waitForMilliSeconds(100);
                i++;
            } while (i < 10);
        }
    }

    /**
     * scroll to the given element
     * we scroll 200 px to the left of the element so that we see all of it
     * @param element
     */
    public void scrollToElement(WebElement element, int yOffset) {
        if (isWebTest) {
            //         ((JavascriptExecutor) driver).executeScript("arguments[0].scrollIntoView(true);", element);
            ((JavascriptExecutor) driver)
                    .executeScript("window.top.scroll(" + Math.max(element.getLocation().x - 200, 0) + ","
                            + Math.max(element.getLocation().y + yOffset, 0) + ")");
        }
    }

    /**
     * TODO: handle mobile app case
     */
    @SuppressWarnings("unchecked")
    public Point getScrollPosition() {
        if (isWebTest) {
            try {
                List<Number> dims = (List<Number>) ((JavascriptExecutor) driver)
                        .executeScript(JS_GET_CURRENT_SCROLL_POSITION);
                return new Point(dims.get(0).intValue(), dims.get(1).intValue());
            } catch (Exception e) {
                return new Point(0, 0);
            }
        } else {
            throw new WebDriverException("scroll position can only be get for web");
        }
    }

    /**
     * Returns the rectangle of all screens on the system
     * @return
     */
    private static Rectangle getScreensRectangle() {
        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();

        Rectangle screenRect = new Rectangle(0, 0, 0, 0);
        for (GraphicsDevice gd : ge.getScreenDevices()) {
            screenRect = screenRect.union(gd.getDefaultConfiguration().getBounds());
        }
        return screenRect;
    }

    /**
     * Take screenshot of the desktop and put it in a file
     * Do not expose this method because we need to check that we have a graphical environment. 
     */
    private static BufferedImage captureDesktopToBuffer() {

        Rectangle screenRect = getScreensRectangle();
        try {
            return new Robot().createScreenCapture(screenRect);
        } catch (AWTException e) {
            throw new ScenarioException("Cannot capture image", e);
        }

        //      Integer screenWidth = defaultGraphicDevice.getDisplayMode().getWidth();
        //      Integer screenHeight = defaultGraphicDevice.getDisplayMode().getHeight();
        //      
        //      // Capture the screen shot of the area of the screen defined by the rectangle
        //      try {
        //         return new Robot().createScreenCapture(new Rectangle(screenWidth, screenHeight));
        //      } catch (AWTException e) {
        //         throw new ScenarioException("Cannot capture image", e);
        //      }
    }

    /**
     * After quitting driver, if it fails, some pids may remain. Kill them
     */
    @Override
    public void quit() {

        // get list of pids we could have to kill. Sometimes, Chrome does not close all its processes
        // so we have to know which processes to kill when driver is still active
        List<Long> pidsToKill = new ArrayList<>();
        if (browserInfo != null && driverMode == DriverMode.LOCAL) {
            pidsToKill.addAll(browserInfo.getAllBrowserSubprocessPids(driverPids));
        }

        try {
            driver.quit();
        } finally {

            // wait for browser processes to stop
            WaitHelper.waitForSeconds(2);

            // only kill processes in local mode
            if (!pidsToKill.isEmpty()) {
                for (Long pid : pidsToKill) {
                    OSUtilityFactory.getInstance().killProcess(pid.toString(), true);
                }
            }

        }
    }

    /**
     * Use copy to clipboard and copy-paste keyboard shortcut to write something on upload window
     */
    public static void uploadFileUsingClipboard(File tempFile) {

        // Copy to clipboard
        Toolkit.getDefaultToolkit().getSystemClipboard()
                .setContents(new StringSelection(tempFile.getAbsolutePath()), null);
        Robot robot;
        try {
            robot = new Robot();

            WaitHelper.waitForSeconds(1);

            //         // Press Enter
            //         robot.keyPress(KeyEvent.VK_ENTER);
            //   
            //         // Release Enter
            //         robot.keyRelease(KeyEvent.VK_ENTER);

            // Press CTRL+V
            robot.keyPress(KeyEvent.VK_CONTROL);
            robot.keyPress(KeyEvent.VK_V);

            // Release CTRL+V
            robot.keyRelease(KeyEvent.VK_CONTROL);
            robot.keyRelease(KeyEvent.VK_V);
            WaitHelper.waitForSeconds(1);

            // Press Enter
            robot.keyPress(KeyEvent.VK_ENTER);
            robot.keyRelease(KeyEvent.VK_ENTER);
        } catch (AWTException e) {
            throw new ScenarioException("could not initialize robot to upload file: " + e.getMessage());
        }
    }

    /**
     * Upload file typing file path directly
     * @param tempFile
     */
    public static void uploadFileUsingKeyboardTyping(File tempFile) {
        try {
            Keyboard keyboard = new Keyboard();
            Robot robot = keyboard.getRobot();

            WaitHelper.waitForSeconds(1);

            //         // Press Enter
            //         robot.keyPress(KeyEvent.VK_ENTER);
            //   
            //         // Release Enter
            //         robot.keyRelease(KeyEvent.VK_ENTER);

            keyboard.typeKeys(tempFile.getAbsolutePath());

            WaitHelper.waitForSeconds(1);

            // Press Enter
            robot.keyPress(KeyEvent.VK_ENTER);
            robot.keyRelease(KeyEvent.VK_ENTER);

        } catch (AWTException e) {
            throw new ScenarioException("could not initialize robot to upload file typing keys: " + e.getMessage());
        }
    }

    public static void uploadFile(String fileName, String base64Content, DriverMode driverMode,
            SeleniumGridConnector gridConnector) throws IOException {

        if (driverMode == DriverMode.LOCAL) {
            byte[] byteArray = base64Content.getBytes();
            File tempFile = new File("tmp/" + fileName);
            byte[] decodeBuffer = Base64.decodeBase64(byteArray);
            FileUtils.writeByteArrayToFile(tempFile, decodeBuffer);

            try {
                uploadFileUsingClipboard(tempFile);
            } catch (IllegalStateException e) {
                uploadFileUsingKeyboardTyping(tempFile);
            }
        } else if (driverMode == DriverMode.GRID && gridConnector != null) {
            gridConnector.uploadFileToBrowser(fileName, base64Content);
        } else {
            throw new ScenarioException("driver supports uploadFile only in local and grid mode");
        }
    }

    /**
     * move mouse taking screen placing into account. Sometimes, coordinates may be negative if first screen has an other screen on the left 
     * @param robot
     */
    private static void moveMouse(Robot robot, int x, int y) {
        Rectangle screenRectangle = getScreensRectangle();
        robot.mouseMove(x + screenRectangle.x, y + screenRectangle.y);
    }

    /**
     * Left clic at coordinates on desktop. Coordinates are from screen point of view
     * @param x
     * @param y
     */
    public static void leftClicOnDesktopAt(int x, int y, DriverMode driverMode,
            SeleniumGridConnector gridConnector) {

        if (driverMode == DriverMode.LOCAL) {
            try {
                Robot robot = new Robot();
                moveMouse(robot, x, y);
                robot.mousePress(InputEvent.BUTTON1_DOWN_MASK);
                robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
            } catch (AWTException e) {
                throw new ScenarioException("leftClicOnDesktopAt: problem using Robot: " + e.getMessage());
            }
        } else if (driverMode == DriverMode.GRID && gridConnector != null) {
            gridConnector.leftClic(x, y);
        } else {
            throw new ScenarioException("driver supports leftClicOnDesktopAt only in local and grid mode");
        }
    }

    /**
     * Left clic at coordinates on desktop. Coordinates are from screen point of view
     * @param x
     * @param y
     */
    public static void doubleClickOnDesktopAt(int x, int y, DriverMode driverMode,
            SeleniumGridConnector gridConnector) {

        if (driverMode == DriverMode.LOCAL) {
            try {
                Robot robot = new Robot();
                moveMouse(robot, x, y);
                robot.mousePress(InputEvent.BUTTON1_DOWN_MASK);
                robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
                robot.delay(10);
                robot.mousePress(InputEvent.BUTTON1_DOWN_MASK);
                robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
            } catch (AWTException e) {
                throw new ScenarioException("doubleClickOnDesktopAt: problem using Robot: " + e.getMessage());
            }
        } else if (driverMode == DriverMode.GRID && gridConnector != null) {
            gridConnector.doubleClick(x, y);
        } else {
            throw new ScenarioException("driver supports doubleClickOnDesktopAt only in local and grid mode");
        }
    }

    /**
     * right clic at coordinates on desktop. Coordinates are from screen point of view
     * @param x
     * @param y
     */
    public static void rightClicOnDesktopAt(int x, int y, DriverMode driverMode,
            SeleniumGridConnector gridConnector) {

        if (driverMode == DriverMode.LOCAL) {
            try {
                Robot robot = new Robot();
                moveMouse(robot, x, y);
                robot.mousePress(InputEvent.BUTTON2_DOWN_MASK);
                robot.mouseRelease(InputEvent.BUTTON2_DOWN_MASK);
            } catch (AWTException e) {
                throw new ScenarioException("rightClicOnDesktopAt: problem using Robot: " + e.getMessage());
            }
        } else if (driverMode == DriverMode.GRID && gridConnector != null) {
            gridConnector.rightClic(x, y);
        } else {
            throw new ScenarioException("driver supports sendKeysToDesktop only in local and grid mode");
        }
    }

    /**
     * write text to desktop.
     * @param textToWrite   text to write
     * @return
     */
    public static void writeToDesktop(String textToWrite, DriverMode driverMode,
            SeleniumGridConnector gridConnector) {
        if (driverMode == DriverMode.LOCAL) {

            try {
                Keyboard keyboard = new Keyboard();
                keyboard.typeKeys(textToWrite);
            } catch (AWTException e) {
                throw new ScenarioException(
                        "writeToDesktop: could not initialize robot to type keys: " + e.getMessage());
            }
        } else if (driverMode == DriverMode.GRID && gridConnector != null) {
            gridConnector.writeText(textToWrite);
        } else {
            throw new ScenarioException("driver supports sendKeysToDesktop only in local and grid mode");
        }
    }

    /**
     * send keys to desktop
     * This is useful for typing special keys like ENTER
     * @param keys
     */
    public static void sendKeysToDesktop(List<Integer> keyCodes, DriverMode driverMode,
            SeleniumGridConnector gridConnector) {
        if (driverMode == DriverMode.LOCAL) {
            try {
                Robot robot = new Robot();

                WaitHelper.waitForSeconds(1);

                for (Integer key : keyCodes) {
                    robot.keyPress(key);
                    robot.keyRelease(key);
                }
            } catch (AWTException e) {
                throw new ScenarioException("could not initialize robot to type keys: " + e.getMessage());
            }
        } else if (driverMode == DriverMode.GRID && gridConnector != null) {
            gridConnector.sendKeysWithKeyboard(keyCodes);
        } else {
            throw new ScenarioException("driver supports sendKeysToDesktop only in local and grid mode");
        }
    }

    /**
     * Returns a Base64 string of the desktop
     * @param driverMode
     * @param gridConnector
     * @return
     */
    public static String captureDesktopToBase64String(DriverMode driverMode, SeleniumGridConnector gridConnector) {
        if (driverMode == DriverMode.LOCAL) {
            BufferedImage bi = captureDesktopToBuffer();
            ByteArrayOutputStream os = new ByteArrayOutputStream();
            OutputStream b64 = new Base64OutputStream(os);
            try {
                ImageIO.write(bi, "png", b64);
                return os.toString("UTF-8");
            } catch (IOException e) {
                return "";
            }
        } else if (driverMode == DriverMode.GRID && gridConnector != null) {
            return gridConnector.captureDesktopToBuffer();
        } else {
            throw new ScenarioException("driver supports captureDesktopToBase64String only in local and grid mode");
        }
    }

    /**
     * Start video capture using VideoRecorder class
     * @param driverMode
     * @param gridConnector
     * @param videoName      name of the video to record so that it's unique. Only used locally. In remote, grid sessionId is used
     */
    public static VideoRecorder startVideoCapture(DriverMode driverMode, SeleniumGridConnector gridConnector,
            File videoFolder, String videoName) {
        if (driverMode == DriverMode.LOCAL) {
            try {
                VideoRecorder recorder = new VideoRecorder(videoFolder, videoName);
                recorder.start();
                return recorder;
            } catch (HeadlessException e) {
                throw new ScenarioException(
                        "could not initialize video capture with headless robot: " + e.getMessage());
            }

        } else if (driverMode == DriverMode.GRID && gridConnector != null) {
            gridConnector.startVideoCapture();
            return new VideoRecorder(videoFolder, videoName, false);
        } else {
            throw new ScenarioException("driver supports captureDesktopToBase64String only in local and grid mode");
        }
    }

    /**
     * Stop video capture using VideoRecorder class
     * @param driverMode
     * @param gridConnector
     * @throws IOException 
     */
    public static File stopVideoCapture(DriverMode driverMode, SeleniumGridConnector gridConnector,
            VideoRecorder recorder) throws IOException {
        if (driverMode == DriverMode.LOCAL && recorder != null) {
            return recorder.stop();
        } else if (driverMode == DriverMode.GRID && gridConnector != null) {
            return gridConnector.stopVideoCapture(
                    Paths.get(recorder.getFolderPath().getAbsolutePath(), recorder.getFileName()).toString());

        } else {
            throw new ScenarioException("driver supports captureDesktopToBase64String only in local and grid mode");
        }
    }

    /**
     * Intercept specific scripts to do some non selenium actions
     * 
     * @deprecated: should be removed (kept here for compatibility with old robots)
     */
    @Override
    public Object executeScript(String script, Object... args) {

        // to we know this command ?
        if (driverMode == DriverMode.LOCAL && NON_JS_UPLOAD_FILE_THROUGH_POPUP.equals(script)) {
            if (args.length != 2) {
                throw new DriverExceptions(
                        "Upload feature through executeScript needs 2 string arguments (file name, base64 content)");
            }
            try {
                uploadFile((String) args[0], (String) args[1], driverMode, gridConnector);
                return null;
            } catch (IOException e) {
                return null;
            }

        } else if (driverMode == DriverMode.LOCAL && NON_JS_CAPTURE_DESKTOP.equals(script)) {
            return captureDesktopToBase64String(driverMode, gridConnector);
        } else {
            return super.executeScript(script, args);
        }
    }

    public List<Long> getDriverPids() {
        return driverPids;
    }

    public BrowserInfo getBrowserInfo() {
        return browserInfo;
    }

    @Override
    public Capabilities getCapabilities() {
        try {
            return ((HasCapabilities) driver).getCapabilities();
        } catch (ClassCastException e) {
            return new MutableCapabilities();
        }
    }

    public BrowserMobProxy getMobProxy() {
        return mobProxy;
    }

    // NEOLOAD //
    public NLWebDriver getNeoloadDriver() {
        return neoloadDriver;
    }
}