最近比较空闲就仔细看了一下Selenium的源码,由于主要是使用WebDriver因此重点关注了一下WebDriver的工做原理。在前一篇blog里已经解释过了WebDriver与以前Selenium的JS注入实现不一样,直接利用了浏览器native support来操做浏览器。因此对于不一样平台,不一样的浏览器,必须依赖一个特定的浏览器的native component来实现把WebDriver API的调用转化为浏览器的native invoke。java
在咱们new一个WebDriver的过程当中,Selenium首先会确认浏览器的native component是否存在可用并且版本匹配。接着就在目标浏览器里启动一整套Web Service,这套Web Service使用了Selenium本身设计定义的协议,名字叫作The WebDriver Wire Protocol。这套协议很是之强大,几乎能够操做浏览器作任何事情,包括打开、关闭、最大化、最小化、元素定位、元素点击、上传文件等等等等。web
WebDriver Wire协议是通用的,也就是说无论是FirefoxDriver仍是ChromeDriver,启动以后都会在某一个端口启动基于这套协议的Web Service。例如FirefoxDriver初始化成功以后,默认会从http://localhost:7055开始,而ChromeDriver则大概是http://localhost:46350之类的。接下来,咱们调用WebDriver的任何API,都须要借助一个ComandExecutor发送一个命令,其实是一个HTTP request给监听端口上的Web Service。在咱们的HTTP request的body中,会以WebDriver Wire协议规定的JSON格式的字符串来告诉Selenium咱们但愿浏览器接下来作社么事情。浏览器
这里笔者初步画了一个图来表示各类WebDriver的工做原理:session
从上图中咱们能够看出,不一样浏览器的WebDriver子类,都须要依赖特定的浏览器原生组件,例如Firefox就须要一个add-on名字叫webdriver.xpi。而IE的话就须要用到一个dll文件来转化Web Service的命令为浏览器native的调用。另外,图中还标明了WebDriver Wire协议是一套基于RESTful的web service。若是不明白什么是RESTful的,能够参见笔者以前另一篇介绍REST的blog(http://blog.csdn.net/ant_yan/article/details/7963517)多线程
关于WebDriver Wire协议的细节,好比但愿了解这套Web Service可以作哪些事情,能够阅读Selenium官方的协议文档, 在Selenium的源码中,咱们能够找到一个HttpCommandExecutor这个类,里面维护了一个Map<String, CommandInfo>,它负责将一个个表明命令的简单字符串key,转化为相应的URL,由于REST的理念是将全部的操做视做一个个状态,每个状态对应一个URI。因此当咱们以特定的URL发送HTTP request给这个RESTful web service以后,它就能解析出须要执行的操做。截取一段源码以下:post
nameToUrl = ImmutableMap.<String, CommandInfo>builder() .put(NEW_SESSION, post("/session")) .put(QUIT, delete("/session/:sessionId")) .put(GET_CURRENT_WINDOW_HANDLE, get("/session/:sessionId/window_handle")) .put(GET_WINDOW_HANDLES, get("/session/:sessionId/window_handles")) .put(GET, post("/session/:sessionId/url")) // The Alert API is still experimental and should not be used. .put(GET_ALERT, get("/session/:sessionId/alert")) .put(DISMISS_ALERT, post("/session/:sessionId/dismiss_alert")) .put(ACCEPT_ALERT, post("/session/:sessionId/accept_alert")) .put(GET_ALERT_TEXT, get("/session/:sessionId/alert_text")) .put(SET_ALERT_VALUE, post("/session/:sessionId/alert_text"))
try { response = new JsonToBeanConverter().convert(Response.class, responseAsText); } catch (ClassCastException e) { if (responseAsText != null && "".equals(responseAsText)) { // The remote server has died, but has already set some headers. // Normally this occurs when the final window of the firefox driver // is closed on OS X. Return null, as the return value _should_ be // being ignored. This is not an elegant solution. return null; } throw new WebDriverException("Cannot convert text to response: " + responseAsText, e); } //...
参考文章:http://blog.csdn.net/ant_ren/article/details/7970793ui