Skip to content

Instantly share code, notes, and snippets.

@jdm
Created January 16, 2017 18:31
Show Gist options
  • Save jdm/e9ddf21af8c3151a859d63e6035fa21b to your computer and use it in GitHub Desktop.
Save jdm/e9ddf21af8c3151a859d63e6035fa21b to your computer and use it in GitHub Desktop.
commit 2b82584c108cf3a8f6f071f8459bc3b216972c68
Author: Josh Matthews <[email protected]>
Date: Fri Jan 13 19:17:40 2017 -0500
Set the proper filename and line number for inline compiled event handlers.
diff --git a/components/script/dom/bindings/utils.rs b/components/script/dom/bindings/utils.rs
index de1ac8f..8384aaf 100644
--- a/components/script/dom/bindings/utils.rs
+++ b/components/script/dom/bindings/utils.rs
@@ -22,8 +22,8 @@ use js::glue::{RUST_FUNCTION_VALUE_TO_JITINFO, RUST_JSID_IS_INT, RUST_JSID_IS_ST
use js::glue::{RUST_JSID_TO_INT, RUST_JSID_TO_STRING, UnwrapObject};
use js::jsapi::{CallArgs, DOMCallbacks, GetGlobalForObjectCrossCompartment};
use js::jsapi::{HandleId, HandleObject, HandleValue, Heap, JSAutoCompartment, JSContext};
-use js::jsapi::{JSJitInfo, JSObject, JSTracer, JSWrapObjectCallbacks};
-use js::jsapi::{JS_DeletePropertyById, JS_EnumerateStandardClasses};
+use js::jsapi::{JSJitInfo, JSObject, JSTracer, JSWrapObjectCallbacks, AutoFilename};
+use js::jsapi::{JS_DeletePropertyById, JS_EnumerateStandardClasses, DescribeScriptedCaller};
use js::jsapi::{JS_ForwardGetPropertyTo, JS_GetLatin1StringCharsAndLength};
use js::jsapi::{JS_GetProperty, JS_GetPrototype, JS_GetReservedSlot, JS_HasProperty};
use js::jsapi::{JS_HasPropertyById, JS_IsExceptionPending, JS_IsGlobalObject};
@@ -32,7 +32,7 @@ use js::jsapi::{JS_StringHasLatin1Chars, MutableHandleValue, ObjectOpResult};
use js::jsval::{JSVal, UndefinedValue};
use js::rust::{GCMethods, ToString, get_object_class, is_dom_class};
use libc;
-use std::ffi::CString;
+use std::ffi::{CString, CStr};
use std::os::raw::c_void;
use std::ptr;
use std::slice;
@@ -513,3 +513,32 @@ unsafe extern "C" fn instance_class_has_proto_at_depth(clasp: *const js::jsapi::
pub const DOM_CALLBACKS: DOMCallbacks = DOMCallbacks {
instanceClassMatchesProto: Some(instance_class_has_proto_at_depth),
};
+
+pub struct ScriptedCaller {
+ pub filename: String,
+ pub line: usize,
+ pub column: usize,
+}
+
+pub fn describe_scripted_caller() -> Option<ScriptedCaller> {
+ let cx = Runtime::get();
+ let mut fname = AutoFilename {
+ ss_: ptr::null_mut(),
+ filename_: [0, 0],
+ };
+ let mut lineno = 0;
+ let mut column = 0;
+ let ok = unsafe {
+ DescribeScriptedCaller(cx, &mut fname, &mut lineno, &mut column)
+ };
+ if ok {
+ Some(ScriptedCaller {
+ filename: unsafe { CStr::from_ptr(fname.get()).to_string_lossy().into() },
+ line: lineno as usize,
+ column: column as usize,
+ })
+ } else {
+ //TODO: get the entry global? how to figure out line/column, in that case?
+ None
+ }
+}
diff --git a/components/script/dom/eventtarget.rs b/components/script/dom/eventtarget.rs
index 09cafb6..81262e6 100644
--- a/components/script/dom/eventtarget.rs
+++ b/components/script/dom/eventtarget.rs
@@ -71,8 +71,9 @@ pub enum ListenerPhase {
#[derive(JSTraceable, Clone, PartialEq)]
struct InternalRawUncompiledHandler {
source: DOMString,
- url: ServoUrl,
+ filename: String,
line: usize,
+ column: usize,
}
/// A representation of an event handler, either compiled or uncompiled raw source, or null.
@@ -348,14 +349,16 @@ impl EventTarget {
/// Store the raw uncompiled event handler for on-demand compilation later.
/// https://html.spec.whatwg.org/multipage/#event-handler-attributes:event-handler-content-attributes-3
pub fn set_event_handler_uncompiled(&self,
- url: ServoUrl,
+ filename: String,
line: usize,
+ column: usize,
ty: &str,
source: DOMString) {
let handler = InternalRawUncompiledHandler {
source: source,
line: line,
- url: url,
+ column: column,
+ filename: filename,
};
self.set_inline_event_listener(Atom::from(ty),
Some(InlineEventListener::Uncompiled(handler)));
@@ -387,7 +390,7 @@ impl EventTarget {
// Step 1.6
let window = document.window();
- let url_serialized = CString::new(handler.url.to_string()).unwrap();
+ let url_serialized = CString::new(handler.filename).unwrap();
let name = CString::new(&**ty).unwrap();
static mut ARG_NAMES: [*const c_char; 1] = [b"event\0" as *const u8 as *const c_char];
diff --git a/components/script/dom/htmlbodyelement.rs b/components/script/dom/htmlbodyelement.rs
index ea9b177..02becb3 100644
--- a/components/script/dom/htmlbodyelement.rs
+++ b/components/script/dom/htmlbodyelement.rs
@@ -9,7 +9,9 @@ use dom::bindings::codegen::Bindings::HTMLBodyElementBinding::{self, HTMLBodyEle
use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
use dom::bindings::inheritance::Castable;
use dom::bindings::js::{LayoutJS, Root};
+use dom::bindings::reflector::DomObject;
use dom::bindings::str::DOMString;
+use dom::bindings::utils::describe_scripted_caller;
use dom::document::Document;
use dom::element::{AttributeMutation, Element, RawLayoutElementHelpers};
use dom::eventtarget::EventTarget;
@@ -169,9 +171,14 @@ impl VirtualMethods for HTMLBodyElement {
&local_name!("onresize") | &local_name!("onunload") | &local_name!("onerror")
=> {
let evtarget = window.upcast::<EventTarget>(); // forwarded event
- let source_line = 1; //TODO(#9604) obtain current JS execution line
- evtarget.set_event_handler_uncompiled(window.get_url(),
- source_line,
+ let caller = describe_scripted_caller();
+ let (filename, line, column) = match caller {
+ Some(c) => (c.filename, c.line, c.column),
+ None => (window.get_url().to_string(), 1, 0),
+ };
+ evtarget.set_event_handler_uncompiled(filename,
+ line,
+ column,
&name[2..],
DOMString::from((**attr.value()).to_owned()));
false
diff --git a/components/script/dom/htmlelement.rs b/components/script/dom/htmlelement.rs
index 6725fdb..f351818 100644
--- a/components/script/dom/htmlelement.rs
+++ b/components/script/dom/htmlelement.rs
@@ -14,7 +14,9 @@ use dom::bindings::error::{Error, ErrorResult};
use dom::bindings::inheritance::{ElementTypeId, HTMLElementTypeId, NodeTypeId};
use dom::bindings::inheritance::Castable;
use dom::bindings::js::{JS, MutNullableJS, Root, RootedReference};
+use dom::bindings::reflector::DomObject;
use dom::bindings::str::DOMString;
+use dom::bindings::utils::describe_scripted_caller;
use dom::cssstyledeclaration::{CSSModificationAccess, CSSStyleDeclaration, CSSStyleOwner};
use dom::document::{Document, FocusType};
use dom::domstringmap::DOMStringMap;
@@ -511,9 +513,14 @@ impl VirtualMethods for HTMLElement {
match (attr.local_name(), mutation) {
(name, AttributeMutation::Set(_)) if name.starts_with("on") => {
let evtarget = self.upcast::<EventTarget>();
- let source_line = 1; //TODO(#9604) get current JS execution line
- evtarget.set_event_handler_uncompiled(window_from_node(self).get_url(),
- source_line,
+ let caller = describe_scripted_caller();
+ let (filename, line, column) = match caller {
+ Some(c) => (c.filename, c.line, c.column),
+ None => (window_from_node(self).get_url().to_string(), 1, 0),
+ };
+ evtarget.set_event_handler_uncompiled(filename,
+ line,
+ column,
&name[2..],
// FIXME(ajeffrey): Convert directly from AttrValue to DOMString
DOMString::from(&**attr.value()));
diff --git a/tests/wpt/mozilla/tests/mozilla/track_line.html b/tests/wpt/mozilla/tests/mozilla/track_line.html
index b181b70..3e037bd 100644
--- a/tests/wpt/mozilla/tests/mozilla/track_line.html
+++ b/tests/wpt/mozilla/tests/mozilla/track_line.html
@@ -7,16 +7,26 @@
setup({allow_uncaught_exception:true});
var t = async_test("error event has proper line number");
var errors = 0;
-var expected_lines = [21, 4];
+var expected_lines = [[location.href, 27],
+ [location.href, 31],
+ [location.href.replace('track_line.html', 'resources/external.js'), 4]];
window.addEventListener('error', t.step_func(function(e) {
assert_true(e instanceof ErrorEvent);
- assert_equals(e.lineno, expected_lines[errors]);
+ var expected = expected_lines[errors];
+ assert_equals(e.filename, expected[0]);
+ assert_equals(e.lineno, expected[1]);
errors++;
- if (errors == 2) {
+ if (errors == expected_lines.length) {
t.done();
}
}), true);
</script>
+<div id="testdiv"></div>
+<script>
+ var div = document.querySelector('#testdiv');
+ div.setAttribute('onclick', 'this_is_a_js_error');
+ div.dispatchEvent(new Event('click'));
+</script>
<script>
this_is_a_js_error
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment