mirror of
https://github.com/servo/servo.git
synced 2025-07-26 00:30:22 +01:00
Finish the JSON clone algorithm
This commit is contained in:
parent
17f423723c
commit
c665a65690
6 changed files with 137 additions and 56 deletions
|
@ -18,7 +18,7 @@ use crate::dom::bindings::conversions::{
|
||||||
use crate::dom::bindings::conversions::{
|
use crate::dom::bindings::conversions::{
|
||||||
ConversionBehavior, ConversionResult, FromJSValConvertible, StringificationBehavior,
|
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::inheritance::Castable;
|
||||||
use crate::dom::bindings::reflector::DomObject;
|
use crate::dom::bindings::reflector::DomObject;
|
||||||
use crate::dom::bindings::root::DomRoot;
|
use crate::dom::bindings::root::DomRoot;
|
||||||
|
@ -39,9 +39,10 @@ use cookie::Cookie;
|
||||||
use euclid::default::{Point2D, Rect, Size2D};
|
use euclid::default::{Point2D, Rect, Size2D};
|
||||||
use hyper_serde::Serde;
|
use hyper_serde::Serde;
|
||||||
use ipc_channel::ipc::{self, IpcSender};
|
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::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::BrowsingContextId;
|
||||||
use msg::constellation_msg::PipelineId;
|
use msg::constellation_msg::PipelineId;
|
||||||
use net_traits::CookieSource::{NonHTTP, HTTP};
|
use net_traits::CookieSource::{NonHTTP, HTTP};
|
||||||
|
@ -52,7 +53,9 @@ use script_traits::webdriver_msg::{
|
||||||
WebDriverFrameId, WebDriverJSError, WebDriverJSResult, WebDriverJSValue,
|
WebDriverFrameId, WebDriverJSError, WebDriverJSResult, WebDriverJSValue,
|
||||||
};
|
};
|
||||||
use servo_url::ServoUrl;
|
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;
|
use webdriver::error::ErrorStatus;
|
||||||
|
|
||||||
fn find_node_by_unique_id(
|
fn find_node_by_unique_id(
|
||||||
|
@ -121,6 +124,31 @@ fn first_matching_link(
|
||||||
.map(|nodes| matching_links(&nodes, link_text, partial).take(1).next())
|
.map(|nodes| matching_links(&nodes, link_text, partial).take(1).next())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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)]
|
#[allow(unsafe_code)]
|
||||||
pub unsafe fn jsval_to_webdriver(
|
pub unsafe fn jsval_to_webdriver(
|
||||||
cx: *mut JSContext,
|
cx: *mut JSContext,
|
||||||
|
@ -157,20 +185,15 @@ pub unsafe fn jsval_to_webdriver(
|
||||||
});
|
});
|
||||||
let _ac = JSAutoRealm::new(cx, *object);
|
let _ac = JSAutoRealm::new(cx, *object);
|
||||||
|
|
||||||
if let Ok(element) = root_from_object::<HTMLElement>(*object, cx) {
|
if is_array_like(cx, val) {
|
||||||
return Ok(WebDriverJSValue::Element(WebElement(
|
let mut result: Vec<WebDriverJSValue> = Vec::new();
|
||||||
element.upcast::<Node>().unique_id(),
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
|
|
||||||
if !is_array_like(cx, val) {
|
let length = match get_property::<u32>(
|
||||||
return Err(WebDriverJSError::UnknownType);
|
cx,
|
||||||
}
|
object.handle(),
|
||||||
|
"length",
|
||||||
let mut result: Vec<WebDriverJSValue> = Vec::new();
|
ConversionBehavior::Default,
|
||||||
|
) {
|
||||||
let length =
|
|
||||||
match get_property::<u32>(cx, object.handle(), "length", ConversionBehavior::Default) {
|
|
||||||
Ok(length) => match length {
|
Ok(length) => match length {
|
||||||
Some(length) => length,
|
Some(length) => length,
|
||||||
_ => return Err(WebDriverJSError::UnknownType),
|
_ => return Err(WebDriverJSError::UnknownType),
|
||||||
|
@ -181,21 +204,76 @@ pub unsafe fn jsval_to_webdriver(
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
for i in 0..length {
|
for i in 0..length {
|
||||||
rooted!(in(cx) let mut item = UndefinedValue());
|
rooted!(in(cx) let mut item = UndefinedValue());
|
||||||
match get_property_jsval(cx, object.handle(), &i.to_string(), item.handle_mut()) {
|
match get_property_jsval(cx, object.handle(), &i.to_string(), item.handle_mut()) {
|
||||||
Ok(_) => match jsval_to_webdriver(cx, global_scope, item.handle()) {
|
Ok(_) => match jsval_to_webdriver(cx, global_scope, item.handle()) {
|
||||||
Ok(converted_item) => result.push(converted_item),
|
Ok(converted_item) => result.push(converted_item),
|
||||||
err @ Err(_) => return err,
|
err @ Err(_) => return err,
|
||||||
},
|
},
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
throw_dom_exception(SafeJSContext::from_ptr(cx), global_scope, error);
|
throw_dom_exception(SafeJSContext::from_ptr(cx), global_scope, error);
|
||||||
return Err(WebDriverJSError::JSError);
|
return Err(WebDriverJSError::JSError);
|
||||||
},
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Ok(WebDriverJSValue::ArrayLike(result))
|
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::Object(result))
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Err(WebDriverJSError::UnknownType)
|
Err(WebDriverJSError::UnknownType)
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,8 @@ use hyper_serde::Serde;
|
||||||
use ipc_channel::ipc::IpcSender;
|
use ipc_channel::ipc::IpcSender;
|
||||||
use msg::constellation_msg::BrowsingContextId;
|
use msg::constellation_msg::BrowsingContextId;
|
||||||
use servo_url::ServoUrl;
|
use servo_url::ServoUrl;
|
||||||
use webdriver::common::WebElement;
|
use std::collections::HashMap;
|
||||||
|
use webdriver::common::{WebElement, WebFrame, WebWindow};
|
||||||
use webdriver::error::ErrorStatus;
|
use webdriver::error::ErrorStatus;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
@ -100,17 +101,21 @@ pub enum WebDriverJSValue {
|
||||||
Number(f64),
|
Number(f64),
|
||||||
String(String),
|
String(String),
|
||||||
Element(WebElement),
|
Element(WebElement),
|
||||||
|
Frame(WebFrame),
|
||||||
|
Window(WebWindow),
|
||||||
ArrayLike(Vec<WebDriverJSValue>),
|
ArrayLike(Vec<WebDriverJSValue>),
|
||||||
|
Object(HashMap<String, WebDriverJSValue>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
pub enum WebDriverJSError {
|
pub enum WebDriverJSError {
|
||||||
Timeout,
|
|
||||||
UnknownType,
|
|
||||||
JSError,
|
|
||||||
/// Occurs when handler received an event message for a layout channel that is not
|
/// Occurs when handler received an event message for a layout channel that is not
|
||||||
/// associated with the current script thread
|
/// associated with the current script thread
|
||||||
BrowsingContextNotFound,
|
BrowsingContextNotFound,
|
||||||
|
JSError,
|
||||||
|
StaleElementReference,
|
||||||
|
Timeout,
|
||||||
|
UnknownType,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type WebDriverJSResult = Result<WebDriverJSValue, WebDriverJSError>;
|
pub type WebDriverJSResult = Result<WebDriverJSValue, WebDriverJSError>;
|
||||||
|
|
|
@ -241,11 +241,18 @@ impl Serialize for SendableWebDriverJSValue {
|
||||||
WebDriverJSValue::Number(x) => serializer.serialize_f64(x),
|
WebDriverJSValue::Number(x) => serializer.serialize_f64(x),
|
||||||
WebDriverJSValue::String(ref x) => serializer.serialize_str(&x),
|
WebDriverJSValue::String(ref x) => serializer.serialize_str(&x),
|
||||||
WebDriverJSValue::Element(ref x) => x.serialize(serializer),
|
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
|
WebDriverJSValue::ArrayLike(ref x) => x
|
||||||
.iter()
|
.iter()
|
||||||
.map(|element| SendableWebDriverJSValue(element.clone()))
|
.map(|element| SendableWebDriverJSValue(element.clone()))
|
||||||
.collect::<Vec<SendableWebDriverJSValue>>()
|
.collect::<Vec<SendableWebDriverJSValue>>()
|
||||||
.serialize(serializer),
|
.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(
|
Ok(value) => Ok(WebDriverResponse::Generic(ValueResponse(
|
||||||
serde_json::to_value(SendableWebDriverJSValue(value))?,
|
serde_json::to_value(SendableWebDriverJSValue(value))?,
|
||||||
))),
|
))),
|
||||||
Err(WebDriverJSError::Timeout) => Err(WebDriverError::new(ErrorStatus::Timeout, "")),
|
Err(WebDriverJSError::BrowsingContextNotFound) => Err(WebDriverError::new(
|
||||||
Err(WebDriverJSError::UnknownType) => Err(WebDriverError::new(
|
ErrorStatus::JavascriptError,
|
||||||
ErrorStatus::UnsupportedOperation,
|
"Pipeline id not found in browsing context",
|
||||||
"Unsupported return type",
|
|
||||||
)),
|
)),
|
||||||
Err(WebDriverJSError::JSError) => Err(WebDriverError::new(
|
Err(WebDriverJSError::JSError) => Err(WebDriverError::new(
|
||||||
ErrorStatus::JavascriptError,
|
ErrorStatus::JavascriptError,
|
||||||
"JS evaluation raised an exception",
|
"JS evaluation raised an exception",
|
||||||
)),
|
)),
|
||||||
Err(WebDriverJSError::BrowsingContextNotFound) => Err(WebDriverError::new(
|
Err(WebDriverJSError::StaleElementReference) => Err(WebDriverError::new(
|
||||||
ErrorStatus::JavascriptError,
|
ErrorStatus::StaleElementReference,
|
||||||
"Pipeline id not found in browsing context",
|
"Stale element",
|
||||||
|
)),
|
||||||
|
Err(WebDriverJSError::Timeout) => Err(WebDriverError::new(ErrorStatus::Timeout, "")),
|
||||||
|
Err(WebDriverJSError::UnknownType) => Err(WebDriverError::new(
|
||||||
|
ErrorStatus::UnsupportedOperation,
|
||||||
|
"Unsupported return type",
|
||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,4 @@
|
||||||
[cyclic.py]
|
[cyclic.py]
|
||||||
[test_object]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[test_array_in_object]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[test_element_in_object]
|
[test_element_in_object]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
[get.py]
|
[get.py]
|
||||||
[test_primitives[js_primitive3-py_primitive3\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[test_idl_attribute]
|
[test_idl_attribute]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -17,9 +14,8 @@
|
||||||
[test_no_browsing_context]
|
[test_no_browsing_context]
|
||||||
expected: ERROR
|
expected: ERROR
|
||||||
|
|
||||||
[test_primitives_set_by_execute_script[js_primitive3-py_primitive3\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[test_primitives_set_by_execute_script["foobar"-foobar\]]
|
[test_primitives_set_by_execute_script["foobar"-foobar\]]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
[test_primitives_set_by_execute_script[js_primitive3-py_primitive3\]]
|
||||||
|
expected: FAIL
|
||||||
|
|
|
@ -2,6 +2,3 @@
|
||||||
[test_no_browsing_context]
|
[test_no_browsing_context]
|
||||||
expected: ERROR
|
expected: ERROR
|
||||||
|
|
||||||
[test_payload]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue