diff options
author | George Roman <george.roman.99@gmail.com> | 2019-08-11 00:37:24 +0300 |
---|---|---|
committer | George Roman <george.roman.99@gmail.com> | 2019-08-21 19:28:32 +0300 |
commit | c665a656900db286df275bd3a0f1a31b307844c1 (patch) | |
tree | 4f36db5c8ec067a88c7d74b2c44f1f203851a127 | |
parent | 17f423723c0206c934f104eb3352f39a9533aa41 (diff) | |
download | servo-c665a656900db286df275bd3a0f1a31b307844c1.tar.gz servo-c665a656900db286df275bd3a0f1a31b307844c1.zip |
Finish the JSON clone algorithm
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 - |