最近比較空閒就仔細看了一下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