/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use devtools_traits::TimelineMarkerType; use devtools_traits::{AutoMargins, CONSOLE_API, CachedConsoleMessage, CachedConsoleMessageTypes}; use devtools_traits::{ComputedNodeLayout, ConsoleAPI, PageError, ScriptToDevtoolsControlMsg}; use devtools_traits::{EvaluateJSReply, Modification, NodeInfo, PAGE_ERROR, TimelineMarker}; use dom::bindings::codegen::Bindings::CSSStyleDeclarationBinding::CSSStyleDeclarationMethods; use dom::bindings::codegen::Bindings::DOMRectBinding::DOMRectMethods; use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods; use dom::bindings::codegen::Bindings::ElementBinding::ElementMethods; use dom::bindings::codegen::Bindings::LocationBinding::LocationMethods; use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; use dom::bindings::conversions::{FromJSValConvertible, jsstring_to_str}; use dom::bindings::global::GlobalRef; use dom::bindings::inheritance::Castable; use dom::bindings::js::Root; use dom::bindings::str::DOMString; use dom::browsingcontext::BrowsingContext; use dom::element::Element; use dom::node::Node; use dom::window::Window; use ipc_channel::ipc::IpcSender; use js::jsapi::{JSAutoCompartment, ObjectClassName, RootedObject, RootedValue}; use js::jsval::UndefinedValue; use msg::constellation_msg::PipelineId; use script_thread::get_browsing_context; use std::ffi::CStr; use std::str; use style::properties::longhands::{margin_top, margin_right, margin_bottom, margin_left}; use uuid::Uuid; #[allow(unsafe_code)] pub fn handle_evaluate_js(global: &GlobalRef, eval: String, reply: IpcSender) { // global.get_cx() returns a valid `JSContext` pointer, so this is safe. let result = unsafe { let cx = global.get_cx(); let globalhandle = global.reflector().get_jsobject(); let _ac = JSAutoCompartment::new(cx, globalhandle.get()); let mut rval = RootedValue::new(cx, UndefinedValue()); global.evaluate_js_on_global_with_result(&eval, rval.handle_mut()); if rval.ptr.is_undefined() { EvaluateJSReply::VoidValue } else if rval.ptr.is_boolean() { EvaluateJSReply::BooleanValue(rval.ptr.to_boolean()) } else if rval.ptr.is_double() || rval.ptr.is_int32() { EvaluateJSReply::NumberValue(FromJSValConvertible::from_jsval(cx, rval.handle(), ()) .unwrap()) } else if rval.ptr.is_string() { EvaluateJSReply::StringValue(String::from(jsstring_to_str(cx, rval.ptr.to_string()))) } else if rval.ptr.is_null() { EvaluateJSReply::NullValue } else { assert!(rval.ptr.is_object()); let obj = RootedObject::new(cx, rval.ptr.to_object()); let class_name = CStr::from_ptr(ObjectClassName(cx, obj.handle())); let class_name = str::from_utf8(class_name.to_bytes()).unwrap(); EvaluateJSReply::ActorValue { class: class_name.to_owned(), uuid: Uuid::new_v4().to_string(), } } }; reply.send(result).unwrap(); } pub fn handle_get_root_node(context: &BrowsingContext, pipeline: PipelineId, reply: IpcSender) { let context = get_browsing_context(context, pipeline); let document = context.active_document(); let node = document.upcast::(); reply.send(node.summarize()).unwrap(); } pub fn handle_get_document_element(context: &BrowsingContext, pipeline: PipelineId, reply: IpcSender) { let context = get_browsing_context(context, pipeline); let document = context.active_document(); let document_element = document.GetDocumentElement().unwrap(); let node = document_element.upcast::(); reply.send(node.summarize()).unwrap(); } fn find_node_by_unique_id(context: &BrowsingContext, pipeline: PipelineId, node_id: String) -> Root { let context = get_browsing_context(context, pipeline); let document = context.active_document(); let node = document.upcast::(); for candidate in node.traverse_preorder() { if candidate.unique_id() == node_id { return candidate; } } panic!("couldn't find node with unique id {}", node_id) } pub fn handle_get_children(context: &BrowsingContext, pipeline: PipelineId, node_id: String, reply: IpcSender>) { let parent = find_node_by_unique_id(context, pipeline, node_id); let children = parent.children() .map(|child| child.summarize()) .collect(); reply.send(children).unwrap(); } pub fn handle_get_layout(context: &BrowsingContext, pipeline: PipelineId, node_id: String, reply: IpcSender) { let node = find_node_by_unique_id(context, pipeline, node_id); let elem = node.downcast::().expect("should be getting layout of element"); let rect = elem.GetBoundingClientRect(); let width = rect.Width() as f32; let height = rect.Height() as f32; let window = context.active_window(); let elem = node.downcast::().expect("should be getting layout of element"); let computed_style = window.r().GetComputedStyle(elem, None); reply.send(ComputedNodeLayout { display: String::from(computed_style.Display()), position: String::from(computed_style.Position()), zIndex: String::from(computed_style.ZIndex()), boxSizing: String::from(computed_style.BoxSizing()), autoMargins: determine_auto_margins(&window, &*node), marginTop: String::from(computed_style.MarginTop()), marginRight: String::from(computed_style.MarginRight()), marginBottom: String::from(computed_style.MarginBottom()), marginLeft: String::from(computed_style.MarginLeft()), borderTopWidth: String::from(computed_style.BorderTopWidth()), borderRightWidth: String::from(computed_style.BorderRightWidth()), borderBottomWidth: String::from(computed_style.BorderBottomWidth()), borderLeftWidth: String::from(computed_style.BorderLeftWidth()), paddingTop: String::from(computed_style.PaddingTop()), paddingRight: String::from(computed_style.PaddingRight()), paddingBottom: String::from(computed_style.PaddingBottom()), paddingLeft: String::from(computed_style.PaddingLeft()), width: width, height: height, }).unwrap(); } fn determine_auto_margins(window: &Window, node: &Node) -> AutoMargins { let margin = window.margin_style_query(node.to_trusted_node_address()); AutoMargins { top: margin.top == margin_top::computed_value::T::Auto, right: margin.right == margin_right::computed_value::T::Auto, bottom: margin.bottom == margin_bottom::computed_value::T::Auto, left: margin.left == margin_left::computed_value::T::Auto, } } pub fn handle_get_cached_messages(_pipeline_id: PipelineId, message_types: CachedConsoleMessageTypes, reply: IpcSender>) { // TODO: check the messageTypes against a global Cache for console messages and page exceptions let mut messages = Vec::new(); if message_types.contains(PAGE_ERROR) { // TODO: make script error reporter pass all reported errors // to devtools and cache them for returning here. let msg = PageError { type_: "PageError".to_owned(), errorMessage: "page error test".to_owned(), sourceName: String::new(), lineText: String::new(), lineNumber: 0, columnNumber: 0, category: String::new(), timeStamp: 0, error: false, warning: false, exception: false, strict: false, private: false, }; messages.push(CachedConsoleMessage::PageError(msg)); } if message_types.contains(CONSOLE_API) { // TODO: do for real let msg = ConsoleAPI { type_: "ConsoleAPI".to_owned(), level: "error".to_owned(), filename: "http://localhost/~mihai/mozilla/test.html".to_owned(), lineNumber: 0, functionName: String::new(), timeStamp: 0, private: false, arguments: vec!["console error test".to_owned()], }; messages.push(CachedConsoleMessage::ConsoleAPI(msg)); } reply.send(messages).unwrap(); } pub fn handle_modify_attribute(context: &BrowsingContext, pipeline: PipelineId, node_id: String, modifications: Vec) { let node = find_node_by_unique_id(context, pipeline, node_id); let elem = node.downcast::().expect("should be getting layout of element"); for modification in modifications { match modification.newValue { Some(string) => { let _ = elem.SetAttribute(DOMString::from(modification.attributeName), DOMString::from(string)); }, None => elem.RemoveAttribute(DOMString::from(modification.attributeName)), } } } pub fn handle_wants_live_notifications(global: &GlobalRef, send_notifications: bool) { global.set_devtools_wants_updates(send_notifications); } pub fn handle_set_timeline_markers(context: &BrowsingContext, marker_types: Vec, reply: IpcSender) { let window = context.active_window(); window.set_devtools_timeline_markers(marker_types, reply); } pub fn handle_drop_timeline_markers(context: &BrowsingContext, marker_types: Vec) { let window = context.active_window(); window.drop_devtools_timeline_markers(marker_types); } pub fn handle_request_animation_frame(context: &BrowsingContext, id: PipelineId, actor_name: String) { let context = context.find(id).expect("There is no such context"); let doc = context.active_document(); let devtools_sender = context.active_window().devtools_chan().unwrap(); doc.request_animation_frame(box move |time| { let msg = ScriptToDevtoolsControlMsg::FramerateTick(actor_name, time); devtools_sender.send(msg).unwrap(); }); } pub fn handle_reload(context: &BrowsingContext, id: PipelineId) { let context = context.find(id).expect("There is no such context"); let win = context.active_window(); let location = win.Location(); location.Reload(); }