diff --git a/java/client/src/org/openqa/selenium/chrome/ChromeCommandExecutor.java b/java/client/src/org/openqa/selenium/chrome/ChromeCommandExecutor.java index f5b6dee6726e3..c50508645a1ab 100644 --- a/java/client/src/org/openqa/selenium/chrome/ChromeCommandExecutor.java +++ b/java/client/src/org/openqa/selenium/chrome/ChromeCommandExecutor.java @@ -19,15 +19,20 @@ package org.openqa.selenium.chrome; import com.google.common.base.Throwables; +import com.google.common.collect.ImmutableMap; import org.openqa.selenium.WebDriverException; +import org.openqa.selenium.chrome.ChromeDriverCommand; import org.openqa.selenium.remote.Command; -import org.openqa.selenium.remote.DriverCommand; +import org.openqa.selenium.remote.CommandInfo; import org.openqa.selenium.remote.HttpCommandExecutor; import org.openqa.selenium.remote.Response; import java.io.IOException; import java.net.ConnectException; +import java.net.URL; +import java.util.Map; +import java.util.HashMap; /** * A specialized {@link HttpCommandExecutor} that will use a {@link ChromeDriverService} that lives @@ -36,19 +41,34 @@ */ class ChromeCommandExecutor extends HttpCommandExecutor { + private static final Map CHROME_COMMANDS_NAME_TO_URL = ImmutableMap.of( + ChromeDriverCommand.LAUNCH_APP, + post("/session/:sessionId/chromium/launch_app")); + private final ChromeDriverService service; /** * Creates a new ChromeCommandExecutor which will communicate with the chromedriver as configured * by the given {@code service}. - * + * * @param service The ChromeDriverService to send commands to. */ public ChromeCommandExecutor(ChromeDriverService service) { - super(service.getUrl()); + super(CHROME_COMMANDS_NAME_TO_URL, service.getUrl()); this.service = service; } + /** + * Creates a new ChromeCommandExecutor which will communicate with the chromedriver as configured + * by the given {@code service}. + * + * @param serviceUrl The URL of the service to communicate with. + */ + public ChromeCommandExecutor(URL serviceUrl) { + super(CHROME_COMMANDS_NAME_TO_URL, serviceUrl); + this.service = null; + } + /** * Sends the {@code command} to the chromedriver server for execution. The server will be started * if requesting a new session. Likewise, if terminating a session, the server will be shutdown @@ -60,7 +80,8 @@ public ChromeCommandExecutor(ChromeDriverService service) { */ @Override public Response execute(Command command) throws IOException { - if (DriverCommand.NEW_SESSION.equals(command.getName())) { + if (ChromeDriverCommand.NEW_SESSION.equals(command.getName()) && + this.service != null) { service.start(); } @@ -76,7 +97,8 @@ public Response execute(Command command) throws IOException { Throwables.propagateIfPossible(t); throw new WebDriverException(t); } finally { - if (DriverCommand.QUIT.equals(command.getName())) { + if (ChromeDriverCommand.QUIT.equals(command.getName()) && + this.service != null) { service.stop(); } } diff --git a/java/client/src/org/openqa/selenium/chrome/ChromeDriver.java b/java/client/src/org/openqa/selenium/chrome/ChromeDriver.java index efed34a008d86..225c9cc3d8048 100644 --- a/java/client/src/org/openqa/selenium/chrome/ChromeDriver.java +++ b/java/client/src/org/openqa/selenium/chrome/ChromeDriver.java @@ -18,10 +18,16 @@ package org.openqa.selenium.chrome; +import com.google.common.collect.ImmutableMap; + +import java.net.URL; + import org.openqa.selenium.Capabilities; import org.openqa.selenium.OutputType; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebDriverException; +import org.openqa.selenium.chrome.ChromeDriverCommand; +import org.openqa.selenium.chrome.ChromeCommandExecutor; import org.openqa.selenium.remote.DriverCommand; import org.openqa.selenium.remote.FileDetector; import org.openqa.selenium.remote.RemoteWebDriver; @@ -160,6 +166,17 @@ public ChromeDriver(ChromeDriverService service, Capabilities capabilities) { super(new DriverCommandExecutor(service), capabilities); } + /** + * Creates a new ChromeDriver instance. The {@code service} will be started along with the + * driver, and shutdown upon calling {@link #quit()}. + * + * @param service The service to use. + * @param capabilities The capabilities required from the ChromeDriver. + */ + public ChromeDriver(URL serviceUrl, Capabilities capabilities) { + super(new ChromeCommandExecutor(serviceUrl), capabilities); + } + @Override public void setFileDetector(FileDetector detector) { throw new WebDriverException( @@ -174,6 +191,10 @@ public X getScreenshotAs(OutputType target) { return target.convertFromBase64Png(base64); } + public void launchApp(String id) { + execute(ChromeDriverCommand.LAUNCH_APP, ImmutableMap.of("id", id)); + } + @Override protected void startSession(Capabilities desiredCapabilities, Capabilities requiredCapabilities) { diff --git a/java/client/src/org/openqa/selenium/chrome/ChromeDriverCommand.java b/java/client/src/org/openqa/selenium/chrome/ChromeDriverCommand.java new file mode 100644 index 0000000000000..9f73da08dfc14 --- /dev/null +++ b/java/client/src/org/openqa/selenium/chrome/ChromeDriverCommand.java @@ -0,0 +1,25 @@ +/* +Copyright 2014 Software Freedom Conservancy + +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 org.openqa.selenium.chrome; + +import org.openqa.selenium.remote.DriverCommand; + +final class ChromeDriverCommand implements DriverCommand { + private ChromeDriverCommand(){} + + static final String LAUNCH_APP = "chromium.launchApp"; +} diff --git a/java/client/src/org/openqa/selenium/remote/HttpCommandExecutor.java b/java/client/src/org/openqa/selenium/remote/HttpCommandExecutor.java index d21e6ad8a4fbe..7557e3c80f256 100644 --- a/java/client/src/org/openqa/selenium/remote/HttpCommandExecutor.java +++ b/java/client/src/org/openqa/selenium/remote/HttpCommandExecutor.java @@ -496,15 +496,15 @@ private Response createResponse(HttpResponse httpResponse, HttpContext context, return response; } - private static CommandInfo get(String url) { + protected static CommandInfo get(String url) { return new CommandInfo(url, HttpVerb.GET); } - private static CommandInfo post(String url) { + protected static CommandInfo post(String url) { return new CommandInfo(url, HttpVerb.POST); } - private static CommandInfo delete(String url) { + protected static CommandInfo delete(String url) { return new CommandInfo(url, HttpVerb.DELETE); } diff --git a/py/selenium/webdriver/chrome/command.py b/py/selenium/webdriver/chrome/command.py new file mode 100644 index 0000000000000..c5b11fa43acf1 --- /dev/null +++ b/py/selenium/webdriver/chrome/command.py @@ -0,0 +1,21 @@ +# Copyright 2014 Software Freedom Conservancy +# +# 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. + +from selenium.webdriver.remote.command import Command + +class ChromeCommand(Command): + """ + Defines Chrome specific commands while including standard WebDriver ones. + """ + LAUNCH_APP = "launchApp" diff --git a/py/selenium/webdriver/chrome/remote_connection.py b/py/selenium/webdriver/chrome/remote_connection.py new file mode 100644 index 0000000000000..d2edfe85ed6a8 --- /dev/null +++ b/py/selenium/webdriver/chrome/remote_connection.py @@ -0,0 +1,23 @@ +# Copyright 2014 Software Freedom Conservancy +# +# 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. + +from selenium.webdriver.remote.remote_connection import RemoteConnection +from selenium.webdriver.chrome.command import ChromeCommand + +class ChromeRemoteConnection(RemoteConnection): + + def __init__(self, remote_server_addr, keep_alive=False): + RemoteConnection.__init__(self, remote_server_addr, keep_alive) + self._commands[ChromeCommand.LAUNCH_APP] =\ + ('POST', '/session/$sessionId/chromium/launch_app') diff --git a/py/selenium/webdriver/chrome/webdriver.py b/py/selenium/webdriver/chrome/webdriver.py index 182702ee18a7a..c6bb11a859257 100644 --- a/py/selenium/webdriver/chrome/webdriver.py +++ b/py/selenium/webdriver/chrome/webdriver.py @@ -15,6 +15,8 @@ # limitations under the License. import base64 +from command import ChromeCommand +from remote_connection import ChromeRemoteConnection from selenium.webdriver.remote.command import Command from selenium.webdriver.remote.webdriver import WebDriver as RemoteWebDriver from selenium.common.exceptions import WebDriverException @@ -61,6 +63,7 @@ def __init__(self, executable_path="chromedriver", port=0, try: RemoteWebDriver.__init__(self, command_executor=self.service.service_url, + remote_connection_client=ChromeRemoteConnection, desired_capabilities=desired_capabilities, keep_alive=True) except: @@ -68,6 +71,13 @@ def __init__(self, executable_path="chromedriver", port=0, raise self._is_remote = False + def launch_app(self, app_id): + """ + Launches an app with the specified id + """ + return self.execute(ChromeCommand.LAUNCH_APP, + {'id': app_id}) + def quit(self): """ Closes the browser and shuts down the ChromeDriver executable diff --git a/py/selenium/webdriver/remote/webdriver.py b/py/selenium/webdriver/remote/webdriver.py index 30fd3e6e85675..fb77cb2a1db4f 100755 --- a/py/selenium/webdriver/remote/webdriver.py +++ b/py/selenium/webdriver/remote/webdriver.py @@ -45,7 +45,8 @@ class WebDriver(object): """ def __init__(self, command_executor='http://127.0.0.1:4444/wd/hub', - desired_capabilities=None, browser_profile=None, proxy=None, keep_alive=False): + desired_capabilities=None, browser_profile=None, proxy=None, keep_alive=False, + remote_connection_client=RemoteConnection): """ Create a new driver that will issue commands using the wire protocol. @@ -62,7 +63,7 @@ def __init__(self, command_executor='http://127.0.0.1:4444/wd/hub', proxy.add_to_capabilities(desired_capabilities) self.command_executor = command_executor if type(self.command_executor) is bytes or type(self.command_executor) is str: - self.command_executor = RemoteConnection(command_executor, keep_alive=keep_alive) + self.command_executor = remote_connection_client(command_executor, keep_alive=keep_alive) self._is_remote = True self.session_id = None self.capabilities = {}