/* * Copyright 2012-2014 eBay Software Foundation and selendroid committers. * * 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 io.selendroid.server; import io.selendroid.server.common.BaseRequestHandler; import io.selendroid.server.common.BaseServlet; import io.selendroid.server.common.Response; import io.selendroid.server.common.SelendroidResponse; import io.selendroid.server.common.StatusCode; import io.selendroid.server.common.exceptions.AppCrashedException; import io.selendroid.server.common.exceptions.StaleElementReferenceException; import io.selendroid.server.common.http.HttpRequest; import io.selendroid.server.common.http.HttpResponse; import io.selendroid.server.common.http.TrafficCounter; import io.selendroid.server.extension.ExtensionLoader; import io.selendroid.server.handler.*; import io.selendroid.server.handler.alert.Alert; import io.selendroid.server.handler.alert.AlertAccept; import io.selendroid.server.handler.alert.AlertDismiss; import io.selendroid.server.handler.alert.AlertSendKeys; import io.selendroid.server.handler.extension.ExtensionCallHandler; import io.selendroid.server.handler.network.GetNetworkConnectionType; import io.selendroid.server.handler.script.ExecuteAsyncScript; import io.selendroid.server.handler.script.ExecuteScript; import io.selendroid.server.handler.timeouts.AsyncTimeoutHandler; import io.selendroid.server.handler.timeouts.SetImplicitWaitTimeout; import io.selendroid.server.handler.timeouts.TimeoutsHandler; import io.selendroid.server.model.DefaultSelendroidDriver; import io.selendroid.server.model.SelendroidDriver; import io.selendroid.server.util.SelendroidLogger; import java.net.URLDecoder; public class AndroidServlet extends BaseServlet { private SelendroidDriver driver = null; protected ExtensionLoader extensionLoader = null; public AndroidServlet(SelendroidDriver driver, ExtensionLoader extensionLoader) { this.driver = driver; this.extensionLoader = extensionLoader; init(); } protected void init() { register(postHandler, new NewSession("/wd/hub/session")); register(getHandler, new ListSessions("/wd/hub/sessions")); register(getHandler, new GetCapabilities("/wd/hub/session/:sessionId")); register(deleteHandler, new DeleteSession("/wd/hub/session/:sessionId")); register(getHandler, new Alert("/wd/hub/session/:sessionId/alert_text")); register(postHandler, new AlertSendKeys("/wd/hub/session/:sessionId/alert_text")); register(postHandler, new AlertAccept("/wd/hub/session/:sessionId/accept_alert")); register(postHandler, new GoBack("/wd/hub/session/:sessionId/back")); register(getHandler, new GetCookies("/wd/hub/session/:sessionId/cookie")); register(postHandler, new AddCookie("/wd/hub/session/:sessionId/cookie")); register(deleteHandler, new DeleteCookies("/wd/hub/session/:sessionId/cookie")); register(deleteHandler, new DeleteNamedCookie("/wd/hub/session/:sessionId/cookie/:name")); register(postHandler, new AlertDismiss("/wd/hub/session/:sessionId/dismiss_alert")); register(postHandler, new FindElement("/wd/hub/session/:sessionId/element")); register(postHandler, new FindElements("/wd/hub/session/:sessionId/elements")); register(getHandler, new GetElementAttribute( "/wd/hub/session/:sessionId/element/:id/attribute/:name")); register(postHandler, new ClearElement("/wd/hub/session/:sessionId/element/:id/clear")); register(postHandler, new ClickElement("/wd/hub/session/:sessionId/element/:id/click")); register(getHandler, new GetElementDisplayed("/wd/hub/session/:sessionId/element/:id/displayed")); register(postHandler, new FindChildElement("/wd/hub/session/:sessionId/element/:id/element")); register(postHandler, new FindChildElements("/wd/hub/session/:sessionId/element/:id/elements")); register(getHandler, new GetElementEnabled("/wd/hub/session/:sessionId/element/:id/enabled")); register(getHandler, new ElementLocation("/wd/hub/session/:sessionId/element/:id/location")); register(getHandler, new GetElementLocationInView("/wd/hub/session/:sessionId/element/:id/location_in_view")); register(getHandler, new GetElementTagName("/wd/hub/session/:sessionId/element/:id/name")); register(getHandler, new GetElementSelected("/wd/hub/session/:sessionId/element/:id/selected")); register(getHandler, new LogElement("/wd/hub/session/:sessionId/element/:id/source")); register(postHandler, new SubmitForm("/wd/hub/session/:sessionId/element/:id/submit")); register(getHandler, new GetText("/wd/hub/session/:sessionId/element/:id/text")); register(postHandler, new SendKeysToElement("/wd/hub/session/:sessionId/element/:id/value")); register(getHandler, new GetElementSize("/wd/hub/session/:sessionId/element/:id/size")); register(postHandler, new ExecuteScript("/wd/hub/session/:sessionId/execute")); register(postHandler, new ExecuteAsyncScript("/wd/hub/session/:sessionId/execute_async")); register(postHandler, new GoForward("/wd/hub/session/:sessionId/forward")); register(postHandler, new FrameSwitchHandler("/wd/hub/session/:sessionId/frame")); register(postHandler, new SendKeys("/wd/hub/session/:sessionId/keys")); register(postHandler, new Refresh("/wd/hub/session/:sessionId/refresh")); register(getHandler, new CaptureScreenshot("/wd/hub/session/:sessionId/screenshot")); register(getHandler, new LogElementTree("/wd/hub/session/:sessionId/source")); register(postHandler, new TimeoutsHandler("/wd/hub/session/:sessionId/timeouts")); register(postHandler, new AsyncTimeoutHandler( "/wd/hub/session/:sessionId/timeouts/async_script")); register(postHandler, new SetImplicitWaitTimeout( "/wd/hub/session/:sessionId/timeouts/implicit_wait")); register(getHandler, new GetPageTitle("/wd/hub/session/:sessionId/title")); register(getHandler, new GetCurrentUrl("/wd/hub/session/:sessionId/url")); register(postHandler, new OpenUrl("/wd/hub/session/:sessionId/url")); register(postHandler, new SwitchContext("/wd/hub/session/:sessionId/window")); register(getHandler, new GetWindowSize("/wd/hub/session/:sessionId/window/:windowHandle/size")); register(getHandler, new GetContext("/wd/hub/session/:sessionId/window_handle")); register(getHandler, new GetContexts("/wd/hub/session/:sessionId/window_handles")); register(getHandler, new GetScreenOrientation("/wd/hub/session/:sessionId/orientation")); register(postHandler, new RotateScreen("/wd/hub/session/:sessionId/orientation")); // Advanced Touch API register(postHandler, new SingleTapOnElement("/wd/hub/session/:sessionId/touch/click")); register(postHandler, new Down("/wd/hub/session/:sessionId/touch/down")); register(postHandler, new Up("/wd/hub/session/:sessionId/touch/up")); register(postHandler, new Move("/wd/hub/session/:sessionId/touch/move")); register(postHandler, new Scroll("/wd/hub/session/:sessionId/touch/scroll")); register(postHandler, new DoubleTapOnElement("/wd/hub/session/:sessionId/touch/doubleclick")); register(postHandler, new LongPressOnElement("/wd/hub/session/:sessionId/touch/longclick")); register(postHandler, new Flick("/wd/hub/session/:sessionId/touch/flick")); // Track-ball functionality register(postHandler, new Roll("/wd/hub/session/:sessionId/trackball/roll")); // The new endpoints for context switching coming with Selenium 3.0 & mobile spec register(getHandler, new GetNetworkConnectionType("/wd/hub/session/:sessionId/network_connection")); register(getHandler, new GetContext("/wd/hub/session/:sessionId/context")); register(getHandler, new GetContexts("/wd/hub/session/:sessionId/contexts")); register(postHandler, new SwitchContext("/wd/hub/session/:sessionId/context")); // Custom extensions to wire protocol register(getHandler, new GetScreenState("/wd/hub/session/:sessionId/selendroid/screen/brightness")); register(postHandler, new SetScreenState("/wd/hub/session/:sessionId/selendroid/screen/brightness")); register(postHandler, new InspectorTap("/wd/hub/session/:sessionId/tap/2")); register(getHandler, new GetCommandConfiguration( "/wd/hub/session/:sessionId/selendroid/configure/command/:command")); register(postHandler, new SetCommandConfiguration( "/wd/hub/session/:sessionId/selendroid/configure/command/:command")); register(postHandler, new ForceGcExplicitly("/wd/hub/session/:sessionId/selendroid/gc")); register(postHandler, new SetSystemProperty("/wd/hub/session/:sessionId/selendroid/systemProperty")); // Endpoints to send app to background and resume it register(postHandler, new BackgroundApp("/wd/hub/session/:sessionId/selendroid/background")); register(postHandler, new ResumeApp("/wd/hub/session/:sessionId/selendroid/resume")); // Endpoints to add to and read call logs register(postHandler, new AddCallLog("/wd/hub/session/:sessionId/selendroid/addCallLog")); register(postHandler, new ReadCallLog("/wd/hub/session/:sessionId/selendroid/readCallLog")); // Handle calls to dynamically loaded handlers register(postHandler, new ExtensionCallHandler( "/wd/hub/session/:sessionId/selendroid/extension", extensionLoader)); // Actions sequencing endpoint register(postHandler, new Actions("/wd/hub/session/:sessionId/actions")); // currently not yet supported register(getHandler, new UnknownCommandHandler( "/wd/hub/session/:sessionId/ime/available_engines")); register(getHandler, new UnknownCommandHandler("/wd/hub/session/:sessionId/ime/active_engine")); register(getHandler, new UnknownCommandHandler("/wd/hub/session/:sessionId/ime/activated")); register(postHandler, new UnknownCommandHandler("/wd/hub/session/:sessionId/ime/deactivate")); register(postHandler, new UnknownCommandHandler("/wd/hub/session/:sessionId/ime/activate")); register(deleteHandler, new UnknownCommandHandler("/wd/hub/session/:sessionId/window")); register(postHandler, new UnknownCommandHandler( "/wd/hub/session/:sessionId/window/:windowHandle/size")); register(postHandler, new UnknownCommandHandler( "/wd/hub/session/:sessionId/window/:windowHandle/position")); register(getHandler, new UnknownCommandHandler( "/wd/hub/session/:sessionId/window/:windowHandle/position")); register(postHandler, new UnknownCommandHandler( "/wd/hub/session/:sessionId/window/:windowHandle/maximize")); register(getHandler, new UnknownCommandHandler("/wd/hub/session/:sessionId/element/:id")); register(postHandler, new UnknownCommandHandler("/wd/hub/session/:sessionId/element/active")); register(getHandler, new UnknownCommandHandler( "/wd/hub/session/:sessionId/element/:id/equals/:other")); register(getHandler, new UnknownCommandHandler( "/wd/hub/session/:sessionId/element/:id/css/:propertyName")); register(postHandler, new UnknownCommandHandler("/wd/hub/session/:sessionId/moveto")); register(postHandler, new UnknownCommandHandler("/wd/hub/session/:sessionId/buttondown")); register(postHandler, new UnknownCommandHandler("/wd/hub/session/:sessionId/buttonup")); register(postHandler, new UnknownCommandHandler("/wd/hub/session/:sessionId/doubleclick")); register(getHandler, new UnknownCommandHandler("/wd/hub/session/:sessionId/location")); register(postHandler, new UnknownCommandHandler("/wd/hub/session/:sessionId/location")); register(getHandler, new UnknownCommandHandler("/wd/hub/session/:sessionId/local_storage")); register(postHandler, new UnknownCommandHandler("/wd/hub/session/:sessionId/local_storage")); register(deleteHandler, new UnknownCommandHandler("/wd/hub/session/:sessionId/local_storage")); register(getHandler, new UnknownCommandHandler( "/wd/hub/session/:sessionId/local_storage/key/:key")); register(deleteHandler, new UnknownCommandHandler( "/wd/hub/session/:sessionId/local_storage/key/:key")); register(getHandler, new UnknownCommandHandler("/wd/hub/session/:sessionId/local_storage/size")); register(getHandler, new UnknownCommandHandler("/wd/hub/session/:sessionId/location")); register(postHandler, new UnknownCommandHandler("/wd/hub/session/:sessionId/location")); register(getHandler, new UnknownCommandHandler("/wd/hub/session/:sessionId/local_storage")); register(postHandler, new UnknownCommandHandler("/wd/hub/session/:sessionId/local_storage")); register(deleteHandler, new UnknownCommandHandler("/wd/hub/session/:sessionId/local_storage")); register(getHandler, new UnknownCommandHandler( "/wd/hub/session/:sessionId/local_storage/key/:key")); register(deleteHandler, new UnknownCommandHandler( "/wd/hub/session/:sessionId/local_storage/key/:key")); register(getHandler, new UnknownCommandHandler("/wd/hub/session/:sessionId/local_storage/size")); register(getHandler, new UnknownCommandHandler("/wd/hub/session/:sessionId/session_storage")); register(postHandler, new UnknownCommandHandler("/wd/hub/session/:sessionId/session_storage")); register(deleteHandler, new UnknownCommandHandler("/wd/hub/session/:sessionId/session_storage")); register(getHandler, new UnknownCommandHandler( "/wd/hub/session/:sessionId/session_storage/key/:key")); register(deleteHandler, new UnknownCommandHandler( "/wd/hub/session/:sessionId/session_storage/key/:key")); register(getHandler, new UnknownCommandHandler( "/wd/hub/session/:sessionId/session_storage/size")); // handled in the standalone-server register(postHandler, new UnknownCommandHandler("/wd/hub/session/:sessionId/log")); register(getHandler, new UnknownCommandHandler("/wd/hub/session/:sessionId/log/types")); } private void addHandlerAttributesToRequest(HttpRequest request, String mappedUri) { String sessionId = getParameter(mappedUri, request.uri(), ":sessionId"); if (sessionId != null) { request.data().put(SESSION_ID_KEY, sessionId); } String command = getParameter(mappedUri, request.uri(), ":command"); if (command != null) { request.data().put(COMMAND_NAME_KEY, command); } String id = getParameter(mappedUri, request.uri(), ":id"); if (id != null) { request.data().put(ELEMENT_ID_KEY, URLDecoder.decode(id)); } String name = getParameter(mappedUri, request.uri(), ":name"); if (name != null) { request.data().put(NAME_ID_KEY, name); } request.data().put(DRIVER_KEY, driver); } @Override public void handleRequest(HttpRequest request, HttpResponse response, BaseRequestHandler handler) { if ("/favicon.ico".equals(request.uri()) && handler == null) { response.setStatus(404).end(); return; } else if (handler == null) { response.setStatus(404).end(); return; } Response result; try { addHandlerAttributesToRequest(request, handler.getMappedUri()); if (!handler.commandAllowedWithAlertPresentInWebViewMode()) { SelendroidDriver driver = (SelendroidDriver) request.data().get(AndroidServlet.DRIVER_KEY); if (driver != null && driver.isAlertPresent()) { result = new SelendroidResponse(handler.getSessionId(request), StatusCode.UNEXPECTED_ALERT_OPEN, "Unhandled Alert present"); handleResponse(request, response, (SelendroidResponse) result); return; } } result = handler.handle(request); } catch (StaleElementReferenceException se) { try { SelendroidLogger.error("StaleElementReferenceException", se); String sessionId = getParameter(handler.getMappedUri(), request.uri(), ":sessionId"); result = new SelendroidResponse(sessionId, StatusCode.STALE_ELEMENT_REFERENCE, se); } catch (Exception e) { SelendroidLogger.error("Error responding to StaleElementReferenceException", e); replyWithServerError(response); return; } } catch (AppCrashedException ae) { try { SelendroidLogger.error("App crashed when handling request", ae); String sessionId = getParameter(handler.getMappedUri(), request.uri(), ":sessionId"); result = new SelendroidResponse(sessionId, StatusCode.UNKNOWN_ERROR, ae); } catch (Exception e) { SelendroidLogger.error("Error responding to app crash", e); replyWithServerError(response); return; } } catch (Exception e) { SelendroidLogger.error("Error handling request.", e); replyWithServerError(response); return; } handleResponse(request, response, (SelendroidResponse) result); String trafficStatistics = String.format( "traffic_stats: rx_bytes %d tx_bytes %d", TrafficCounter.readBytes(), TrafficCounter.writtenBytes()); SelendroidLogger.info(trafficStatistics); } }