aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGeorge Roman <george.roman.99@gmail.com>2019-08-11 00:37:24 +0300
committerGeorge Roman <george.roman.99@gmail.com>2019-08-21 19:28:32 +0300
commitc665a656900db286df275bd3a0f1a31b307844c1 (patch)
tree4f36db5c8ec067a88c7d74b2c44f1f203851a127
parent17f423723c0206c934f104eb3352f39a9533aa41 (diff)
downloadservo-c665a656900db286df275bd3a0f1a31b307844c1.tar.gz
servo-c665a656900db286df275bd3a0f1a31b307844c1.zip
Finish the JSON clone algorithm
-rw-r--r--components/script/webdriver_handlers.rs136
-rw-r--r--components/script_traits/webdriver_msg.rs13
-rw-r--r--components/webdriver_server/lib.rs25
-rw-r--r--tests/wpt/metadata/webdriver/tests/execute_script/cyclic.py.ini6
-rw-r--r--tests/wpt/metadata/webdriver/tests/get_element_property/get.py.ini8
-rw-r--r--tests/wpt/metadata/webdriver/tests/get_window_rect/get.py.ini3
6 files changed, 136 insertions, 55 deletions
diff --git a/components/script/webdriver_handlers.rs b/components/script/webdriver_handlers.rs
index 564dc7ceb4d..7293774b7a4 100644
--- a/components/script/webdriver_handlers.rs
+++ b/components/script/webdriver_handlers.rs
@@ -18,7 +18,7 @@ use crate::dom::bindings::conversions::{
use crate::dom::bindings::conversions::{
ConversionBehavior, ConversionResult, FromJSValConvertible, StringificationBehavior,
};
-use crate::dom::bindings::error::throw_dom_exception;
+use crate::dom::bindings::error::{throw_dom_exception, Error};
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::reflector::DomObject;
use crate::dom::bindings::root::DomRoot;
@@ -39,9 +39,10 @@ use cookie::Cookie;
use euclid::default::{Point2D, Rect, Size2D};
use hyper_serde::Serde;
use ipc_channel::ipc::{self, IpcSender};
-use js::jsapi::{JSAutoRealm, JSContext};
+use js::jsapi::{HandleValueArray, JSAutoRealm, JSContext, JSType, JS_IsExceptionPending};
use js::jsval::UndefinedValue;
-use js::rust::HandleValue;
+use js::rust::wrappers::{JS_CallFunctionName, JS_GetProperty, JS_HasOwnProperty, JS_TypeOfValue};
+use js::rust::{Handle, HandleObject, HandleValue};
use msg::constellation_msg::BrowsingContextId;
use msg::constellation_msg::PipelineId;
use net_traits::CookieSource::{NonHTTP, HTTP};
@@ -52,7 +53,9 @@ use script_traits::webdriver_msg::{
WebDriverFrameId, WebDriverJSError, WebDriverJSResult, WebDriverJSValue,
};
use servo_url::ServoUrl;
-use webdriver::common::WebElement;
+use std::collections::HashMap;
+use std::ffi::CString;
+use webdriver::common::{WebElement, WebFrame, WebWindow};
use webdriver::error::ErrorStatus;
fn find_node_by_unique_id(
@@ -122,6 +125,31 @@ fn first_matching_link(
}
#[allow(unsafe_code)]
+unsafe fn object_has_to_json_property(
+ cx: *mut JSContext,
+ global_scope: &GlobalScope,
+ object: HandleObject,
+) -> bool {
+ let name = CString::new("toJSON").unwrap();
+ let mut found = false;
+ if JS_HasOwnProperty(cx, object, name.as_ptr(), &mut found) && found {
+ rooted!(in(cx) let mut value = UndefinedValue());
+ let result = JS_GetProperty(cx, object, name.as_ptr(), value.handle_mut());
+ if !result {
+ throw_dom_exception(SafeJSContext::from_ptr(cx), global_scope, Error::JSFailed);
+ false
+ } else {
+ result && JS_TypeOfValue(cx, value.handle()) == JSType::JSTYPE_FUNCTION
+ }
+ } else if JS_IsExceptionPending(cx) {
+ throw_dom_exception(SafeJSContext::from_ptr(cx), global_scope, Error::JSFailed);
+ false
+ } else {
+ false
+ }
+}
+
+#[allow(unsafe_code)]
pub unsafe fn jsval_to_webdriver(
cx: *mut JSContext,
global_scope: &GlobalScope,
@@ -157,20 +185,15 @@ pub unsafe fn jsval_to_webdriver(
});
let _ac = JSAutoRealm::new(cx, *object);
- if let Ok(element) = root_from_object::<HTMLElement>(*object, cx) {
- return Ok(WebDriverJSValue::Element(WebElement(
- element.upcast::<Node>().unique_id(),
- )));
- }
+ if is_array_like(cx, val) {
+ let mut result: Vec<WebDriverJSValue> = Vec::new();
- if !is_array_like(cx, val) {
- return Err(WebDriverJSError::UnknownType);
- }
-
- let mut result: Vec<WebDriverJSValue> = Vec::new();
-
- let length =
- match get_property::<u32>(cx, object.handle(), "length", ConversionBehavior::Default) {
+ let length = match get_property::<u32>(
+ cx,
+ object.handle(),
+ "length",
+ ConversionBehavior::Default,
+ ) {
Ok(length) => match length {
Some(length) => length,
_ => return Err(WebDriverJSError::UnknownType),
@@ -181,21 +204,76 @@ pub unsafe fn jsval_to_webdriver(
},
};
- for i in 0..length {
- rooted!(in(cx) let mut item = UndefinedValue());
- match get_property_jsval(cx, object.handle(), &i.to_string(), item.handle_mut()) {
- Ok(_) => match jsval_to_webdriver(cx, global_scope, item.handle()) {
- Ok(converted_item) => result.push(converted_item),
- err @ Err(_) => return err,
- },
- Err(error) => {
- throw_dom_exception(SafeJSContext::from_ptr(cx), global_scope, error);
+ for i in 0..length {
+ rooted!(in(cx) let mut item = UndefinedValue());
+ match get_property_jsval(cx, object.handle(), &i.to_string(), item.handle_mut()) {
+ Ok(_) => match jsval_to_webdriver(cx, global_scope, item.handle()) {
+ Ok(converted_item) => result.push(converted_item),
+ err @ Err(_) => return err,
+ },
+ Err(error) => {
+ throw_dom_exception(SafeJSContext::from_ptr(cx), global_scope, error);
+ return Err(WebDriverJSError::JSError);
+ },
+ }
+ }
+
+ Ok(WebDriverJSValue::ArrayLike(result))
+ } else if let Ok(element) = root_from_object::<Element>(*object, cx) {
+ Ok(WebDriverJSValue::Element(WebElement(
+ element.upcast::<Node>().unique_id(),
+ )))
+ } else if let Ok(window) = root_from_object::<Window>(*object, cx) {
+ let window_proxy = window.window_proxy();
+ if window_proxy.is_browsing_context_discarded() {
+ Err(WebDriverJSError::StaleElementReference)
+ } else if window_proxy.browsing_context_id() ==
+ window_proxy.top_level_browsing_context_id()
+ {
+ Ok(WebDriverJSValue::Window(WebWindow(
+ window.Document().upcast::<Node>().unique_id(),
+ )))
+ } else {
+ Ok(WebDriverJSValue::Frame(WebFrame(
+ window.Document().upcast::<Node>().unique_id(),
+ )))
+ }
+ } else if object_has_to_json_property(cx, global_scope, object.handle()) {
+ let name = CString::new("toJSON").unwrap();
+ rooted!(in(cx) let mut value = UndefinedValue());
+ if JS_CallFunctionName(
+ cx,
+ object.handle(),
+ name.as_ptr(),
+ &mut HandleValueArray::new(),
+ value.handle_mut(),
+ ) {
+ jsval_to_webdriver(cx, global_scope, Handle::new(&value))
+ } else {
+ throw_dom_exception(SafeJSContext::from_ptr(cx), global_scope, Error::JSFailed);
+ Err(WebDriverJSError::JSError)
+ }
+ } else {
+ let mut result = HashMap::new();
+
+ let common_properties = vec!["x", "y", "width", "height", "key"];
+ for property in common_properties.iter() {
+ rooted!(in(cx) let mut item = UndefinedValue());
+ if let Ok(_) = get_property_jsval(cx, object.handle(), property, item.handle_mut())
+ {
+ if !item.is_undefined() {
+ if let Ok(value) = jsval_to_webdriver(cx, global_scope, item.handle()) {
+ result.insert(property.to_string(), value);
+ }
+ }
+ } else {
+ throw_dom_exception(SafeJSContext::from_ptr(cx), global_scope, Error::JSFailed);
return Err(WebDriverJSError::JSError);
- },
+ }
}
- }
- Ok(WebDriverJSValue::ArrayLike(result))
+ Ok(WebDriverJSValue::Object(result))
+ }
} else {
Err(WebDriverJSError::UnknownType)
}
diff --git a/components/script_traits/webdriver_msg.rs b/components/script_traits/webdriver_msg.rs
index 9716bd220d6..23fa60efddb 100644
--- a/components/script_traits/webdriver_msg.rs
+++ b/components/script_traits/webdriver_msg.rs
@@ -10,7 +10,8 @@ use hyper_serde::Serde;
use ipc_channel::ipc::IpcSender;
use msg::constellation_msg::BrowsingContextId;
use servo_url::ServoUrl;
-use webdriver::common::WebElement;
+use std::collections::HashMap;
+use webdriver::common::{WebElement, WebFrame, WebWindow};
use webdriver::error::ErrorStatus;
#[derive(Debug, Deserialize, Serialize)]
@@ -100,17 +101,21 @@ pub enum WebDriverJSValue {
Number(f64),
String(String),
Element(WebElement),
+ Frame(WebFrame),
+ Window(WebWindow),
ArrayLike(Vec<WebDriverJSValue>),
+ Object(HashMap<String, WebDriverJSValue>),
}
#[derive(Debug, Deserialize, Serialize)]
pub enum WebDriverJSError {
- Timeout,
- UnknownType,
- JSError,
/// Occurs when handler received an event message for a layout channel that is not
/// associated with the current script thread
BrowsingContextNotFound,
+ JSError,
+ StaleElementReference,
+ Timeout,
+ UnknownType,
}
pub type WebDriverJSResult = Result<WebDriverJSValue, WebDriverJSError>;
diff --git a/components/webdriver_server/lib.rs b/components/webdriver_server/lib.rs
index 0bd7d140c32..3b5d223c3fb 100644
--- a/components/webdriver_server/lib.rs
+++ b/components/webdriver_server/lib.rs
@@ -241,11 +241,18 @@ impl Serialize for SendableWebDriverJSValue {
WebDriverJSValue::Number(x) => serializer.serialize_f64(x),
WebDriverJSValue::String(ref x) => serializer.serialize_str(&x),
WebDriverJSValue::Element(ref x) => x.serialize(serializer),
+ WebDriverJSValue::Frame(ref x) => x.serialize(serializer),
+ WebDriverJSValue::Window(ref x) => x.serialize(serializer),
WebDriverJSValue::ArrayLike(ref x) => x
.iter()
.map(|element| SendableWebDriverJSValue(element.clone()))
.collect::<Vec<SendableWebDriverJSValue>>()
.serialize(serializer),
+ WebDriverJSValue::Object(ref x) => x
+ .iter()
+ .map(|(k, v)| (k.clone(), SendableWebDriverJSValue(v.clone())))
+ .collect::<HashMap<String, SendableWebDriverJSValue>>()
+ .serialize(serializer),
}
}
}
@@ -1396,18 +1403,22 @@ impl Handler {
Ok(value) => Ok(WebDriverResponse::Generic(ValueResponse(
serde_json::to_value(SendableWebDriverJSValue(value))?,
))),
- Err(WebDriverJSError::Timeout) => Err(WebDriverError::new(ErrorStatus::Timeout, "")),
- Err(WebDriverJSError::UnknownType) => Err(WebDriverError::new(
- ErrorStatus::UnsupportedOperation,
- "Unsupported return type",
+ Err(WebDriverJSError::BrowsingContextNotFound) => Err(WebDriverError::new(
+ ErrorStatus::JavascriptError,
+ "Pipeline id not found in browsing context",
)),
Err(WebDriverJSError::JSError) => Err(WebDriverError::new(
ErrorStatus::JavascriptError,
"JS evaluation raised an exception",
)),
- Err(WebDriverJSError::BrowsingContextNotFound) => Err(WebDriverError::new(
- ErrorStatus::JavascriptError,
- "Pipeline id not found in browsing context",
+ Err(WebDriverJSError::StaleElementReference) => Err(WebDriverError::new(
+ ErrorStatus::StaleElementReference,
+ "Stale element",
+ )),
+ Err(WebDriverJSError::Timeout) => Err(WebDriverError::new(ErrorStatus::Timeout, "")),
+ Err(WebDriverJSError::UnknownType) => Err(WebDriverError::new(
+ ErrorStatus::UnsupportedOperation,
+ "Unsupported return type",
)),
}
}
diff --git a/tests/wpt/metadata/webdriver/tests/execute_script/cyclic.py.ini b/tests/wpt/metadata/webdriver/tests/execute_script/cyclic.py.ini
index 15a6f71fb26..81f7b00c745 100644
--- a/tests/wpt/metadata/webdriver/tests/execute_script/cyclic.py.ini
+++ b/tests/wpt/metadata/webdriver/tests/execute_script/cyclic.py.ini
@@ -1,10 +1,4 @@
[cyclic.py]
- [test_object]
- expected: FAIL
-
- [test_array_in_object]
- expected: FAIL
-
[test_element_in_object]
expected: FAIL
diff --git a/tests/wpt/metadata/webdriver/tests/get_element_property/get.py.ini b/tests/wpt/metadata/webdriver/tests/get_element_property/get.py.ini
index d6b7c0acee8..8d73d60b865 100644
--- a/tests/wpt/metadata/webdriver/tests/get_element_property/get.py.ini
+++ b/tests/wpt/metadata/webdriver/tests/get_element_property/get.py.ini
@@ -1,7 +1,4 @@
[get.py]
- [test_primitives[js_primitive3-py_primitive3\]]
- expected: FAIL
-
[test_idl_attribute]
expected: FAIL
@@ -17,9 +14,8 @@
[test_no_browsing_context]
expected: ERROR
- [test_primitives_set_by_execute_script[js_primitive3-py_primitive3\]]
- expected: FAIL
-
[test_primitives_set_by_execute_script["foobar"-foobar\]]
expected: FAIL
+ [test_primitives_set_by_execute_script[js_primitive3-py_primitive3\]]
+ expected: FAIL
diff --git a/tests/wpt/metadata/webdriver/tests/get_window_rect/get.py.ini b/tests/wpt/metadata/webdriver/tests/get_window_rect/get.py.ini
index 810217c99ac..6cf1e1da10a 100644
--- a/tests/wpt/metadata/webdriver/tests/get_window_rect/get.py.ini
+++ b/tests/wpt/metadata/webdriver/tests/get_window_rect/get.py.ini
@@ -2,6 +2,3 @@
[test_no_browsing_context]
expected: ERROR
- [test_payload]
- expected: FAIL
-