Selenium源碼分析之WebDriver 分類: Selenium 2015-07-16 00:20 18人閱讀 評論(0) 收藏

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

能夠看到實際發送的URL都是相對路徑,後綴多以/session/:sessionId開頭,這也意味着WebDriver每次啓動瀏覽器都會分配一個獨立的sessionId,多線程並行的時候彼此之間不會有衝突和干擾。例如咱們最經常使用的一個WebDriver的API,getWebElement在這裏就會轉化爲/session/:sessionId/element這個URL,而後在發出的HTTP request body內再附上具體的參數好比by ID仍是CSS仍是Xpath,各自的值又是什麼。收到並執行了這個操做以後,也會回覆一個HTTP response。內容也是JSON,會返回找到的WebElement的各類細節,好比text、CSS selector、tag name、class name等等。如下是解析咱們說的HTTP response的代碼片斷:

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);
      } //...



相信總結道這裏,應該對WebDriver的運行原理應該清楚了!其實挺佩服這一套RESTful web service的設計。感受封裝WebDriver暴露出來的public API還能夠更加友好跟強大一點。



參考文章:http://blog.csdn.net/ant_ren/article/details/7970793ui

相關文章
相關標籤/搜索