
1// Licensed to the Software Freedom Conservancy (SFC) under one2// or more contributor license agreements.  See the NOTICE file3// distributed with this work for additional information4// regarding copyright ownership.  The SFC licenses this file5// to you under the Apache License, Version 2.0 (the6// "License"); you may not use this file except in compliance7// with the License.  You may obtain a copy of the License at8//9//   http://www.apache.org/licenses/LICENSE-2.010//11// Unless required by applicable law or agreed to in writing,12// software distributed under the License is distributed on an13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY14// KIND, either express or implied.  See the License for the15// specific language governing permissions and limitations16// under the License.17package org.openqa.selenium.chromium;18import com.google.common.collect.ImmutableMap;19import org.openqa.selenium.BuildInfo;20import org.openqa.selenium.Capabilities;21import org.openqa.selenium.Credentials;22import org.openqa.selenium.HasAuthentication;23import org.openqa.selenium.WebDriver;24import org.openqa.selenium.WebDriverException;25import org.openqa.selenium.devtools.CdpInfo;26import org.openqa.selenium.devtools.CdpVersionFinder;27import org.openqa.selenium.devtools.Connection;28import org.openqa.selenium.devtools.DevTools;29import org.openqa.selenium.devtools.HasDevTools;30import org.openqa.selenium.devtools.noop.NoOpCdpInfo;31import org.openqa.selenium.html5.LocalStorage;32import org.openqa.selenium.html5.Location;33import org.openqa.selenium.html5.LocationContext;34import org.openqa.selenium.html5.SessionStorage;35import org.openqa.selenium.html5.WebStorage;36import org.openqa.selenium.interactions.HasTouchScreen;37import org.openqa.selenium.interactions.TouchScreen;38import org.openqa.selenium.internal.Require;39import org.openqa.selenium.logging.EventType;40import org.openqa.selenium.logging.HasLogEvents;41import org.openqa.selenium.mobile.NetworkConnection;42import org.openqa.selenium.remote.CommandExecutor;43import org.openqa.selenium.remote.FileDetector;44import org.openqa.selenium.remote.RemoteTouchScreen;45import org.openqa.selenium.remote.RemoteWebDriver;46import org.openqa.selenium.remote.html5.RemoteLocationContext;47import org.openqa.selenium.remote.html5.RemoteWebStorage;48import org.openqa.selenium.remote.http.HttpClient;49import org.openqa.selenium.remote.mobile.RemoteNetworkConnection;50import java.net.URI;51import java.util.Map;52import java.util.Optional;53import java.util.function.Predicate;54import java.util.function.Supplier;55import java.util.logging.Logger;56/**57 * A {@link WebDriver} implementation that controls a Chromium browser running on the local machine.58 * This class is provided as a convenience for easily testing the Chromium browser. The control server59 * which each instance communicates with will live and die with the instance.60 * <p>61 * To avoid unnecessarily restarting the ChromiumDriver server with each instance, use a62 * {@link RemoteWebDriver} coupled with the desired WebDriverService, which is managed63 * separately.64 * <p>65 * Note that unlike ChromiumDriver, RemoteWebDriver doesn't directly implement66 * role interfaces such as {@link LocationContext} and {@link WebStorage}.67 * Therefore, to access that functionality, it needs to be68 * {@link org.openqa.selenium.remote.Augmenter augmented} and then cast69 * to the appropriate interface.70 */71public class ChromiumDriver extends RemoteWebDriver implements72  HasAuthentication,73  HasDevTools,74  HasLogEvents,75  HasTouchScreen,76  LocationContext,77  NetworkConnection,78  WebStorage {79  private static final Logger LOG = Logger.getLogger(ChromiumDriver.class.getName());80  private final RemoteLocationContext locationContext;81  private final RemoteWebStorage webStorage;82  private final TouchScreen touchScreen;83  private final RemoteNetworkConnection networkConnection;84  private final Optional<Connection> connection;85  private final Optional<DevTools> devTools;86  protected ChromiumDriver(CommandExecutor commandExecutor, Capabilities capabilities, String capabilityKey) {87    super(commandExecutor, capabilities);88    locationContext = new RemoteLocationContext(getExecuteMethod());89    webStorage = new RemoteWebStorage(getExecuteMethod());90    touchScreen = new RemoteTouchScreen(getExecuteMethod());91    networkConnection = new RemoteNetworkConnection(getExecuteMethod());92    HttpClient.Factory factory = HttpClient.Factory.createDefault();93    connection = ChromiumDevToolsLocator.getChromeConnector(94      factory,95      getCapabilities(),96      capabilityKey);97    CdpInfo cdpInfo = new CdpVersionFinder().match(getCapabilities().getBrowserVersion())98      .orElseGet(() -> {99        LOG.warning(100          String.format(101            "Unable to find version of CDP to use for %s. You may need to " +102              "include a dependency on a specific version of the CDP using " +103              "something similar to " +104              "`org.seleniumhq.selenium:selenium-devtools-v86:%s` where the " +105              "version (\"v86\") matches the version of the chromium-based browser " +106              "you're using and the version number of the artifact is the same " +107              "as Selenium's.",108            capabilities.getBrowserVersion(),109            new BuildInfo().getReleaseLabel()));110        return new NoOpCdpInfo();111      });112    devTools = connection.map(conn -> new DevTools(cdpInfo::getDomains, conn));113  }114  @Override115  public void setFileDetector(FileDetector detector) {116    throw new WebDriverException(117      "Setting the file detector only works on remote webdriver instances obtained " +118        "via RemoteWebDriver");119  }120  @Override121  public <X> void onLogEvent(EventType<X> kind) {122    Require.nonNull("Event type", kind);123    kind.initializeListener(this);124  }125  @Override126  public void register(Predicate<URI> whenThisMatches, Supplier<Credentials> useTheseCredentials) {127    Require.nonNull("Check to use to see how we should authenticate", whenThisMatches);128    Require.nonNull("Credentials to use when authenticating", useTheseCredentials);129    getDevTools().createSessionIfThereIsNotOne();130    getDevTools().getDomains().network().addAuthHandler(whenThisMatches, useTheseCredentials);131  }132  @Override133  public LocalStorage getLocalStorage() {134    return webStorage.getLocalStorage();135  }136  @Override137  public SessionStorage getSessionStorage() {138    return webStorage.getSessionStorage();139  }140  @Override141  public Location location() {142    return locationContext.location();143  }144  @Override145  public void setLocation(Location location) {146    locationContext.setLocation(location);147  }148  @Override149  public TouchScreen getTouch() {150    return touchScreen;151  }152  @Override153  public ConnectionType getNetworkConnection() {154    return networkConnection.getNetworkConnection();155  }156  @Override157  public ConnectionType setNetworkConnection(ConnectionType type) {158    return networkConnection.setNetworkConnection(type);159  }160  /**161   * Launches Chrome app specified by id.162   *163   * @param id Chrome app id.164   */165  public void launchApp(String id) {166    execute(ChromiumDriverCommand.LAUNCH_APP, ImmutableMap.of("id", id));167  }168  /**169   * Execute a Chrome Devtools Protocol command and get returned result. The170   * command and command args should follow171   * <a href="https://chromedevtools.github.io/devtools-protocol/">chrome172   * devtools protocol domains/commands</a>.173   */174  public Map<String, Object> executeCdpCommand(String commandName, Map<String, Object> parameters) {175    Require.nonNull("Command name", commandName);176    Require.nonNull("Parameters", parameters);177    @SuppressWarnings("unchecked")178    Map<String, Object> toReturn = (Map<String, Object>) getExecuteMethod().execute(179      ChromiumDriverCommand.EXECUTE_CDP_COMMAND,180      ImmutableMap.of("cmd", commandName, "params", parameters));181    return ImmutableMap.copyOf(toReturn);182  }183  @Override184  public DevTools getDevTools() {185    return devTools.orElseThrow(() -> new WebDriverException("Unable to create DevTools connection"));186  }187  public String getCastSinks() {188    Object response = getExecuteMethod().execute(ChromiumDriverCommand.GET_CAST_SINKS, null);189    return response.toString();190  }191  public String getCastIssueMessage() {192    Object response = getExecuteMethod().execute(ChromiumDriverCommand.GET_CAST_ISSUE_MESSAGE, null);193    return response.toString();194  }195  public void selectCastSink(String deviceName) {196    getExecuteMethod().execute(ChromiumDriverCommand.SET_CAST_SINK_TO_USE, ImmutableMap.of("sinkName", deviceName));197  }198  public void startTabMirroring(String deviceName) {199    getExecuteMethod().execute(ChromiumDriverCommand.START_CAST_TAB_MIRRORING, ImmutableMap.of("sinkName", deviceName));200  }201  public void stopCasting(String deviceName) {202    getExecuteMethod().execute(ChromiumDriverCommand.STOP_CASTING, ImmutableMap.of("sinkName", deviceName));203  }204  public void setPermission(String name, String value) {205    getExecuteMethod().execute(ChromiumDriverCommand.SET_PERMISSION,206      ImmutableMap.of("descriptor", ImmutableMap.of("name", name), "state", value));207  }208  @Override209  public void quit() {210    connection.ifPresent(Connection::close);211    super.quit();212  }213}...
