假设我们使用JAVA,element是WebElement的一个实例,这样开始:
element.getAttribute("row");
在内部,元素有一个透明的ID,服务器用于识别是哪一个元素。为了便于讨论,我们假设这个ID有一个值some_opaque_id,被加密成JAVA命令对象,使用MAP映射到一个参数ID和name,表示用于要查询的属性名的元素ID和name。
快速看一下正确的URL是:
/session/:sessionId/element/:id/attribute/:name
以冒号开始的URL的任何部分假定是一个需要被替换的变量,我们已经获得ID和name,当一个服务器可以同时处理多个会话(Firefox driver不能)时,那么sessionId是另一种用于路由功能的不透明的句柄,这个URL因此像这样扩展:
http://localhost:7055/hub/session/XXX/element/some_opaque_id/attribute/row
顺便说一句,webdriver的远程线协议初是在与URL模板草案提出时同时开发的,我们以明确URL及URL模板的方案允许在一个URL里进行变量扩展(派生)。遗憾的是,虽然URL模板同时提出,我们只意识到他们相对晚了,因此他们不会被用作设计线协议。
因为我们正执行的方法是idempotent4,正确的HTTP方法是使用get,我们委托一个能处理HTTP(Apache的HTTP客户端)来调用服务器的Java库。
Firefox driver被实现成一个Firefox扩展,它的基本设计如上图所示。有些不同寻常的是,它有一个嵌入式HTTP服务器,虽然初我们使用我们自己创建的,在XPCOM上写的HTTP服务器并不是我们的核心竞争力之一,所以机会出现时,我们使用基本的由Mozilla自己写的HTTPD取代了它,HTTPD收到请求马上传递给调度对象。
调度器接收请求并遍历一个已知支持的URL列表,尝试找到相匹配的请求,这种匹配完成了在客户端上的变量插值,一旦精确匹配到,包括使用的动作,JSON对象,意味着在构造要执行的命令。我们的案例中看起来像:
{
'name': 'getElementAttribute',
'sessionId': { 'value': 'XXX' },
'parameters': {
'id': 'some_opaque_key',
'name': 'rows'
}
然后将其作为一个JSON字符串传递到一个自定义的XPCOM组件中,我们叫它CommandProcessor。
var jsonResponseString = JSON.stringify(json);
var callback = function(jsonResponseString) {
var jsonResponse = JSON.parse(jsonResponseString);
if (jsonResponse.status != ErrorCode.SUCCESS) {
response.setStatus(Response.INTERNAL_ERROR);
}
response.setContentType('application/json');
response.setBody(jsonResponseString);
response.commit();
};
// Dispatch the command.
Components.classes['@googlecode.com/webdriver/command-processor;1'].
getService(Components.interfaces.nsICommandProcessor).
execute(jsonString, callback);
这里有大量的代码,但是有两个关键点。首先,我们把上面的对象转换成JSON字符串,其次传递一个回调的执行方法来发送HTTP响应。