diff options
Diffstat (limited to 'components/script')
-rw-r--r-- | components/script/dom/bindings/callback.rs | 34 | ||||
-rw-r--r-- | components/script/dom/bindings/codegen/CodegenRust.py | 44 | ||||
-rw-r--r-- | components/script/dom/browsercontext.rs | 125 | ||||
-rw-r--r-- | components/script/dom/element.rs | 17 | ||||
-rw-r--r-- | components/script/dom/eventdispatcher.rs | 8 | ||||
-rw-r--r-- | components/script/dom/treewalker.rs | 4 | ||||
-rw-r--r-- | components/script/dom/window.rs | 7 | ||||
-rw-r--r-- | components/script/lib.rs | 2 | ||||
-rw-r--r-- | components/script/textinput.rs | 168 | ||||
-rw-r--r-- | components/script/timers.rs | 4 |
10 files changed, 346 insertions, 67 deletions
diff --git a/components/script/dom/bindings/callback.rs b/components/script/dom/bindings/callback.rs index 29741117383..6b8fcc09e34 100644 --- a/components/script/dom/bindings/callback.rs +++ b/components/script/dom/bindings/callback.rs @@ -9,23 +9,20 @@ use dom::bindings::global::global_object_for_js_object; use dom::bindings::js::JSRef; use dom::bindings::utils::Reflectable; -use js::jsapi::{JSContext, JSObject, JS_WrapObject, JS_ObjectIsCallable}; -use js::jsapi::JS_GetProperty; +use js::jsapi::{JSContext, JSObject, JS_WrapObject, JS_ObjectIsCallable, JS_GetGlobalObject}; +use js::jsapi::{JS_GetProperty, JS_IsExceptionPending, JS_ReportPendingException}; use js::jsval::{JSVal, UndefinedValue}; +use js::rust::with_compartment; use std::ptr; /// The exception handling used for a call. -#[deriving(Copy)] +#[deriving(Copy, PartialEq)] pub enum ExceptionHandling { /// Report any exception and don't throw it to the caller code. - ReportExceptions, - /// Throw an exception to the caller code if the thrown exception is a - /// binding object for a DOMError from the caller's scope, otherwise report - /// it. - RethrowContentExceptions, + Report, /// Throw any exception to the caller code. - RethrowExceptions + Rethrow } /// A common base class for representing IDL callback function types. @@ -139,7 +136,7 @@ pub struct CallSetup { /// The `JSContext` used for the call. cx: *mut JSContext, /// The exception handling used for the call. - _handling: ExceptionHandling + _handling: ExceptionHandling, } impl CallSetup { @@ -149,9 +146,10 @@ impl CallSetup { let global = global_object_for_js_object(callback.callback()); let global = global.root(); let cx = global.r().get_cx(); + CallSetup { cx: cx, - _handling: handling + _handling: handling, } } @@ -160,3 +158,17 @@ impl CallSetup { self.cx } } + +impl Drop for CallSetup { + fn drop(&mut self) { + let need_to_deal_with_exception = unsafe { JS_IsExceptionPending(self.cx) } != 0; + if need_to_deal_with_exception { + unsafe { + let old_global = JS_GetGlobalObject(self.cx); + with_compartment(self.cx, old_global, || { + JS_ReportPendingException(self.cx) + }); + } + } + } +} diff --git a/components/script/dom/bindings/codegen/CodegenRust.py b/components/script/dom/bindings/codegen/CodegenRust.py index c1c4aca60ca..1cc9c2dc9e8 100644 --- a/components/script/dom/bindings/codegen/CodegenRust.py +++ b/components/script/dom/bindings/codegen/CodegenRust.py @@ -4837,12 +4837,11 @@ class FakeMember(): return None class CallbackMember(CGNativeMember): - def __init__(self, sig, name, descriptorProvider, needThisHandling, rethrowContentException=False): + def __init__(self, sig, name, descriptorProvider, needThisHandling): """ needThisHandling is True if we need to be able to accept a specified thisObj, False otherwise. """ - assert not rethrowContentException or not needThisHandling self.retvalType = sig[0] self.originalSig = sig @@ -4861,7 +4860,6 @@ class CallbackMember(CGNativeMember): # If needThisHandling, we generate ourselves as private and the caller # will handle generating public versions that handle the "this" stuff. visibility = "priv" if needThisHandling else "pub" - self.rethrowContentException = rethrowContentException # We don't care, for callback codegen, whether our original member was # a method or attribute or whatnot. Just always pass FakeMember() # here. @@ -4984,11 +4982,8 @@ class CallbackMember(CGNativeMember): if not self.needThisHandling: # Since we don't need this handling, we're the actual method that # will be called, so we need an aRethrowExceptions argument. - if self.rethrowContentException: - args.append(Argument("JSCompartment*", "aCompartment", "nullptr")) - else: - args.append(Argument("ExceptionHandling", "aExceptionHandling", - "ReportExceptions")) + args.append(Argument("ExceptionHandling", "aExceptionHandling", + "ReportExceptions")) return args # We want to allow the caller to pass in a "this" object, as # well as a JSContext. @@ -4999,22 +4994,12 @@ class CallbackMember(CGNativeMember): if self.needThisHandling: # It's been done for us already return "" - callSetup = "CallSetup s(CallbackPreserveColor(), aRv" - if self.rethrowContentException: - # getArgs doesn't add the aExceptionHandling argument but does add - # aCompartment for us. - callSetup += ", RethrowContentExceptions, aCompartment" - else: - callSetup += ", aExceptionHandling" - callSetup += ");" - return string.Template( - "${callSetup}\n" + return ( + "CallSetup s(CallbackPreserveColor(), aRv, aExceptionHandling);\n" "JSContext* cx = s.GetContext();\n" "if (!cx) {\n" " return Err(FailureUnknown);\n" - "}\n").substitute({ - "callSetup": callSetup, - }) + "}\n") def getArgcDecl(self): return CGGeneric("let mut argc = %s as u32;" % self.argCountStr); @@ -5035,9 +5020,9 @@ class CallbackMember(CGNativeMember): idlObject.location)) class CallbackMethod(CallbackMember): - def __init__(self, sig, name, descriptorProvider, needThisHandling, rethrowContentException=False): + def __init__(self, sig, name, descriptorProvider, needThisHandling): CallbackMember.__init__(self, sig, name, descriptorProvider, - needThisHandling, rethrowContentException) + needThisHandling) def getRvalDecl(self): return "let mut rval = UndefinedValue();\n" @@ -5076,10 +5061,10 @@ class CallbackOperationBase(CallbackMethod): """ Common class for implementing various callback operations. """ - def __init__(self, signature, jsName, nativeName, descriptor, singleOperation, rethrowContentException=False): + def __init__(self, signature, jsName, nativeName, descriptor, singleOperation): self.singleOperation = singleOperation self.methodName = jsName - CallbackMethod.__init__(self, signature, nativeName, descriptor, singleOperation, rethrowContentException) + CallbackMethod.__init__(self, signature, nativeName, descriptor, singleOperation) def getThisObj(self): if not self.singleOperation: @@ -5117,8 +5102,7 @@ class CallbackOperation(CallbackOperationBase): jsName = method.identifier.name CallbackOperationBase.__init__(self, signature, jsName, MakeNativeName(jsName), - descriptor, descriptor.interface.isSingleOperationInterface(), - rethrowContentException=descriptor.interface.isJSImplemented()) + descriptor, descriptor.interface.isSingleOperationInterface()) class CallbackGetter(CallbackMember): def __init__(self, attr, descriptor): @@ -5128,8 +5112,7 @@ class CallbackGetter(CallbackMember): (attr.type, []), callbackGetterName(attr), descriptor, - needThisHandling=False, - rethrowContentException=descriptor.interface.isJSImplemented()) + needThisHandling=False) def getRvalDecl(self): return "JS::Rooted<JS::Value> rval(cx, JS::UndefinedValue());\n" @@ -5152,8 +5135,7 @@ class CallbackSetter(CallbackMember): [FakeArgument(attr.type, attr)]), callbackSetterName(attr), descriptor, - needThisHandling=False, - rethrowContentException=descriptor.interface.isJSImplemented()) + needThisHandling=False) def getRvalDecl(self): # We don't need an rval diff --git a/components/script/dom/browsercontext.rs b/components/script/dom/browsercontext.rs index 9cc2a94fdb9..959c021b4bc 100644 --- a/components/script/dom/browsercontext.rs +++ b/components/script/dom/browsercontext.rs @@ -2,14 +2,27 @@ * 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 dom::bindings::js::{JS, JSRef, Temporary}; +use dom::bindings::conversions::unwrap_jsmanaged; +use dom::bindings::conversions::{ToJSValConvertible}; +use dom::bindings::js::{JS, JSRef, Temporary, Root}; +use dom::bindings::js::{OptionalRootable, OptionalRootedRootable, ResultRootable}; +use dom::bindings::js::{OptionalRootedReference, OptionalOptionalRootedRootable}; +use dom::bindings::proxyhandler::{getPropertyDescriptor, FillPropertyDescriptor}; use dom::bindings::utils::{Reflectable, WindowProxyHandler}; +use dom::bindings::utils::{GetArrayIndexFromId}; use dom::document::{Document, DocumentHelpers}; use dom::window::Window; - -use js::jsapi::JSObject; +use dom::window::WindowHelpers; + +use js::jsapi::{JSContext, JSObject, jsid, JSPropertyDescriptor}; +use js::jsapi::{JS_AlreadyHasOwnPropertyById, JS_ForwardGetPropertyTo}; +use js::jsapi::{JS_GetPropertyDescriptorById, JS_DefinePropertyById}; +use js::jsapi::{JS_SetPropertyById}; +use js::jsval::JSVal; +use js::glue::{GetProxyPrivate}; use js::glue::{WrapperNew, CreateWrapperProxyHandler, ProxyTraps}; use js::rust::with_compartment; +use js::{JSRESOLVE_QUALIFIED, JSRESOLVE_ASSIGNING}; use std::ptr; @@ -86,18 +99,112 @@ impl SessionHistoryEntry { } } +unsafe fn GetSubframeWindow(cx: *mut JSContext, proxy: *mut JSObject, id: jsid) -> Option<Temporary<Window>> { + let index = GetArrayIndexFromId(cx, id); + if let Some(index) = index { + let target = GetProxyPrivate(proxy).to_object(); + let win: Root<Window> = unwrap_jsmanaged(target).unwrap().root(); + let mut found = false; + return win.r().IndexedGetter(index, &mut found); + } + + None +} + +unsafe extern fn getOwnPropertyDescriptor(cx: *mut JSContext, proxy: *mut JSObject, id: jsid, set: bool, desc: *mut JSPropertyDescriptor) -> bool { + let window = GetSubframeWindow(cx, proxy, id); + if let Some(window) = window { + let window = window.root(); + (*desc).value = window.to_jsval(cx); + FillPropertyDescriptor(&mut *desc, proxy, true); + return true; + } + + let target = GetProxyPrivate(proxy).to_object(); + let flags = if set { JSRESOLVE_ASSIGNING } else { 0 } | JSRESOLVE_QUALIFIED; + // XXX This should be JS_GetOwnPropertyDescriptorById + if JS_GetPropertyDescriptorById(cx, target, id, flags, desc) == 0 { + return false; + } + + if (*desc).obj != target { + // Not an own property + (*desc).obj = ptr::null_mut(); + } else { + (*desc).obj = proxy; + } + + true +} + + +unsafe extern fn defineProperty(cx: *mut JSContext, proxy: *mut JSObject, id: jsid, desc: *mut JSPropertyDescriptor) -> bool { + if GetArrayIndexFromId(cx, id).is_some() { + // Spec says to Reject whether this is a supported index or not, + // since we have no indexed setter or indexed creator. That means + // throwing in strict mode (FIXME: Bug 828137), doing nothing in + // non-strict mode. + return true; + } + + let target = GetProxyPrivate(proxy).to_object(); + JS_DefinePropertyById(cx, target, id, (*desc).value, (*desc).getter, + (*desc).setter, (*desc).attrs) != 0 +} + +unsafe extern fn hasOwn(cx: *mut JSContext, proxy: *mut JSObject, id: jsid, bp: *mut bool) -> bool { + let window = GetSubframeWindow(cx, proxy, id); + if window.is_some() { + *bp = true; + return true; + } + + let target = GetProxyPrivate(proxy).to_object(); + let mut found = 0; + if JS_AlreadyHasOwnPropertyById(cx, target, id, &mut found) == 0 { + return false; + } + + *bp = found != 0; + return true; +} + +unsafe extern fn get(cx: *mut JSContext, proxy: *mut JSObject, receiver: *mut JSObject, id: jsid, vp: *mut JSVal) -> bool { + let window = GetSubframeWindow(cx, proxy, id); + if let Some(window) = window { + let window = window.root(); + *vp = window.to_jsval(cx); + return true; + } + + let target = GetProxyPrivate(proxy).to_object(); + JS_ForwardGetPropertyTo(cx, target, id, receiver, vp) != 0 +} + +unsafe extern fn set(cx: *mut JSContext, proxy: *mut JSObject, _receiver: *mut JSObject, id: jsid, _strict: bool, vp: *mut JSVal) -> bool { + if GetArrayIndexFromId(cx, id).is_some() { + // Reject (which means throw if and only if strict) the set. + // FIXME: Throw + return true; + } + + // FIXME: The receiver should be used, we need something like JS_ForwardSetPropertyTo. + let target = GetProxyPrivate(proxy).to_object(); + JS_SetPropertyById(cx, target, id, vp) != 0 +} + static PROXY_HANDLER: ProxyTraps = ProxyTraps { - getPropertyDescriptor: None, - getOwnPropertyDescriptor: None, - defineProperty: None, + getPropertyDescriptor: Some(getPropertyDescriptor), + getOwnPropertyDescriptor: Some(getOwnPropertyDescriptor), + defineProperty: Some(defineProperty), getOwnPropertyNames: None, delete_: None, enumerate: None, has: None, - hasOwn: None, - get: None, - set: None, + hasOwn: Some(hasOwn), + get: Some(get), + set: Some(set), keys: None, iterate: None, diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs index df99f4dc91d..129756c1575 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -55,6 +55,8 @@ use style::{IntegerAttribute, LengthAttribute, ParserContext, matches}; use servo_util::namespace; use servo_util::str::{DOMString, LengthOrPercentageOrAuto}; +use html5ever::tree_builder::{NoQuirks, LimitedQuirks, Quirks}; + use cssparser::RGBA; use std::ascii::AsciiExt; use std::cell::{Ref, RefMut}; @@ -571,7 +573,7 @@ pub trait AttributeHandlers { value: DOMString) -> AttrValue; fn remove_attribute(self, namespace: Namespace, name: &str); - fn has_class(&self, name: &Atom) -> bool; + fn has_class(self, name: &Atom) -> bool; fn set_atomic_attribute(self, name: &Atom, value: DOMString); @@ -713,10 +715,19 @@ impl<'a> AttributeHandlers for JSRef<'a, Element> { }; } - fn has_class(&self, name: &Atom) -> bool { + fn has_class(self, name: &Atom) -> bool { + let quirks_mode = { + let node: JSRef<Node> = NodeCast::from_ref(self); + let owner_doc = node.owner_doc().root(); + owner_doc.r().quirks_mode() + }; + let is_equal = |lhs: &Atom, rhs: &Atom| match quirks_mode { + NoQuirks | LimitedQuirks => lhs == rhs, + Quirks => lhs.as_slice().eq_ignore_ascii_case(rhs.as_slice()) + }; self.get_attribute(ns!(""), &atom!("class")).root().map(|attr| { attr.r().value().tokens().map(|tokens| { - tokens.iter().any(|atom| atom == name) + tokens.iter().any(|atom| is_equal(name, atom)) }).unwrap_or(false) }).unwrap_or(false) } diff --git a/components/script/dom/eventdispatcher.rs b/components/script/dom/eventdispatcher.rs index d76c3002d8f..3518ff20eb6 100644 --- a/components/script/dom/eventdispatcher.rs +++ b/components/script/dom/eventdispatcher.rs @@ -2,7 +2,7 @@ * 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 dom::bindings::callback::ExceptionHandling::ReportExceptions; +use dom::bindings::callback::ExceptionHandling::Report; use dom::bindings::codegen::Bindings::EventBinding::EventMethods; use dom::bindings::codegen::InheritTypes::{EventTargetCast, NodeCast, NodeDerived}; use dom::bindings::js::{JS, JSRef, OptionalRootable, Root}; @@ -48,7 +48,7 @@ pub fn dispatch_event<'a, 'b>(target: JSRef<'a, EventTarget>, event.set_current_target(cur_target.r()); for listener in listeners.iter() { // Explicitly drop any exception on the floor. - let _ = listener.HandleEvent_(cur_target.r(), event, ReportExceptions); + let _ = listener.HandleEvent_(cur_target.r(), event, Report); if event.stop_immediate() { break; @@ -74,7 +74,7 @@ pub fn dispatch_event<'a, 'b>(target: JSRef<'a, EventTarget>, for listeners in opt_listeners.iter() { for listener in listeners.iter() { // Explicitly drop any exception on the floor. - let _ = listener.HandleEvent_(target, event, ReportExceptions); + let _ = listener.HandleEvent_(target, event, Report); if event.stop_immediate() { break; @@ -93,7 +93,7 @@ pub fn dispatch_event<'a, 'b>(target: JSRef<'a, EventTarget>, event.set_current_target(cur_target.r()); for listener in listeners.iter() { // Explicitly drop any exception on the floor. - let _ = listener.HandleEvent_(cur_target.r(), event, ReportExceptions); + let _ = listener.HandleEvent_(cur_target.r(), event, Report); if event.stop_immediate() { break; diff --git a/components/script/dom/treewalker.rs b/components/script/dom/treewalker.rs index 8cae9d08269..b578823acd6 100644 --- a/components/script/dom/treewalker.rs +++ b/components/script/dom/treewalker.rs @@ -2,7 +2,7 @@ * 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 dom::bindings::callback::ExceptionHandling::RethrowExceptions; +use dom::bindings::callback::ExceptionHandling::Rethrow; use dom::bindings::codegen::Bindings::TreeWalkerBinding; use dom::bindings::codegen::Bindings::TreeWalkerBinding::TreeWalkerMethods; use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods; @@ -325,7 +325,7 @@ impl<'a> PrivateTreeWalkerHelpers<'a> for JSRef<'a, TreeWalker> { match self.filter { Filter::None => Ok(NodeFilterConstants::FILTER_ACCEPT), Filter::Native(f) => Ok((f)(node)), - Filter::JS(callback) => callback.AcceptNode_(self, node, RethrowExceptions) + Filter::JS(callback) => callback.AcceptNode_(self, node, Rethrow) } } diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index 91d023eec18..91402fe6c1f 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -311,12 +311,12 @@ impl<'a> WindowMethods for JSRef<'a, Window> { } } - pub trait WindowHelpers { fn flush_layout(self, goal: ReflowGoal, query: ReflowQueryType); fn init_browser_context(self, doc: JSRef<Document>); fn load_url(self, href: DOMString); fn handle_fire_timer(self, timer_id: TimerId); + fn IndexedGetter(self, _index: u32, _found: &mut bool) -> Option<Temporary<Window>>; } pub trait ScriptHelpers { @@ -378,6 +378,11 @@ impl<'a> WindowHelpers for JSRef<'a, Window> { self.timers.fire_timer(timer_id, self); self.flush_layout(ReflowGoal::ForDisplay, ReflowQueryType::NoQuery); } + + // https://html.spec.whatwg.org/multipage/browsers.html#accessing-other-browsing-contexts + fn IndexedGetter(self, _index: u32, _found: &mut bool) -> Option<Temporary<Window>> { + None + } } impl Window { diff --git a/components/script/lib.rs b/components/script/lib.rs index 50661a60ca1..7593a3b68a3 100644 --- a/components/script/lib.rs +++ b/components/script/lib.rs @@ -62,7 +62,7 @@ pub mod dom { pub mod callback; pub mod error; pub mod conversions; - mod proxyhandler; + pub mod proxyhandler; pub mod str; pub mod structuredclone; pub mod trace; diff --git a/components/script/textinput.rs b/components/script/textinput.rs index 03f8f95b435..809fc56de2d 100644 --- a/components/script/textinput.rs +++ b/components/script/textinput.rs @@ -130,7 +130,7 @@ impl TextInput { fn replace_selection(&mut self, insert: String) { let (begin, end) = self.get_sorted_selection(); - self.selection_begin = None; + self.clear_selection(); let new_lines = { let prefix = self.lines[begin.line].slice_chars(0, begin.index); @@ -181,7 +181,7 @@ impl TextInput { self.selection_begin = Some(self.edit_point); } } else { - self.selection_begin = None; + self.clear_selection(); } assert!(self.edit_point.line < self.lines.len()); @@ -214,7 +214,7 @@ impl TextInput { if self.selection_begin.is_some() { let (begin, end) = self.get_sorted_selection(); self.edit_point = if adjust < 0 {begin} else {end}; - self.selection_begin = None; + self.clear_selection(); return } } @@ -262,6 +262,11 @@ impl TextInput { self.edit_point.index = self.lines[last_line].char_len(); } + /// Remove the current selection. + fn clear_selection(&mut self) { + self.selection_begin = None; + } + /// Process a given `KeyboardEvent` and return an action for the caller to execute. pub fn handle_keydown(&mut self, event: JSRef<KeyboardEvent>) -> KeyReaction { //A simple way to convert an event to a selection @@ -361,3 +366,160 @@ impl TextInput { } } } + +#[test] +fn test_textinput_delete_char() { + let mut textinput = TextInput::new(Lines::Single, "abcdefg".into_string()); + textinput.adjust_horizontal(2, Selection::NotSelected); + textinput.delete_char(DeleteDir::Backward); + assert_eq!(textinput.get_content().as_slice(), "acdefg"); + + textinput.delete_char(DeleteDir::Forward); + assert_eq!(textinput.get_content().as_slice(), "adefg"); + + textinput.adjust_horizontal(2, Selection::Selected); + textinput.delete_char(DeleteDir::Forward); + assert_eq!(textinput.get_content().as_slice(), "afg"); +} + +#[test] +fn test_textinput_insert_char() { + let mut textinput = TextInput::new(Lines::Single, "abcdefg".into_string()); + textinput.adjust_horizontal(2, Selection::NotSelected); + textinput.insert_char('a'); + assert_eq!(textinput.get_content().as_slice(), "abacdefg"); + + textinput.adjust_horizontal(2, Selection::Selected); + textinput.insert_char('b'); + assert_eq!(textinput.get_content().as_slice(), "ababefg"); +} + +#[test] +fn test_textinput_get_sorted_selection() { + let mut textinput = TextInput::new(Lines::Single, "abcdefg".into_string()); + textinput.adjust_horizontal(2, Selection::NotSelected); + textinput.adjust_horizontal(2, Selection::Selected); + let (begin, end) = textinput.get_sorted_selection(); + assert_eq!(begin.index, 2); + assert_eq!(end.index, 4); + + textinput.clear_selection(); + + textinput.adjust_horizontal(-2, Selection::Selected); + let (begin, end) = textinput.get_sorted_selection(); + assert_eq!(begin.index, 2); + assert_eq!(end.index, 4); +} + +#[test] +fn test_textinput_replace_selection() { + let mut textinput = TextInput::new(Lines::Single, "abcdefg".into_string()); + textinput.adjust_horizontal(2, Selection::NotSelected); + textinput.adjust_horizontal(2, Selection::Selected); + + textinput.replace_selection("xyz".into_string()); + assert_eq!(textinput.get_content().as_slice(), "abxyzefg"); +} + +#[test] +fn test_textinput_current_line_length() { + let mut textinput = TextInput::new(Lines::Multiple, "abc\nde\nf".into_string()); + assert_eq!(textinput.current_line_length(), 3); + + textinput.adjust_vertical(1, Selection::NotSelected); + assert_eq!(textinput.current_line_length(), 2); + + textinput.adjust_vertical(1, Selection::NotSelected); + assert_eq!(textinput.current_line_length(), 1); +} + +#[test] +fn test_textinput_adjust_vertical() { + let mut textinput = TextInput::new(Lines::Multiple, "abc\nde\nf".into_string()); + textinput.adjust_horizontal(3, Selection::NotSelected); + textinput.adjust_vertical(1, Selection::NotSelected); + assert_eq!(textinput.edit_point.line, 1); + assert_eq!(textinput.edit_point.index, 2); + + textinput.adjust_vertical(-1, Selection::NotSelected); + assert_eq!(textinput.edit_point.line, 0); + assert_eq!(textinput.edit_point.index, 2); + + textinput.adjust_vertical(2, Selection::NotSelected); + assert_eq!(textinput.edit_point.line, 2); + assert_eq!(textinput.edit_point.index, 1); +} + +#[test] +fn test_textinput_adjust_horizontal() { + let mut textinput = TextInput::new(Lines::Multiple, "abc\nde\nf".into_string()); + textinput.adjust_horizontal(4, Selection::NotSelected); + assert_eq!(textinput.edit_point.line, 1); + assert_eq!(textinput.edit_point.index, 0); + + textinput.adjust_horizontal(1, Selection::NotSelected); + assert_eq!(textinput.edit_point.line, 1); + assert_eq!(textinput.edit_point.index, 1); + + textinput.adjust_horizontal(2, Selection::NotSelected); + assert_eq!(textinput.edit_point.line, 2); + assert_eq!(textinput.edit_point.index, 0); + + textinput.adjust_horizontal(-1, Selection::NotSelected); + assert_eq!(textinput.edit_point.line, 1); + assert_eq!(textinput.edit_point.index, 2); +} + +#[test] +fn test_textinput_handle_return() { + let mut single_line_textinput = TextInput::new(Lines::Single, "abcdef".into_string()); + single_line_textinput.adjust_horizontal(3, Selection::NotSelected); + single_line_textinput.handle_return(); + assert_eq!(single_line_textinput.get_content().as_slice(), "abcdef"); + + let mut multi_line_textinput = TextInput::new(Lines::Multiple, "abcdef".into_string()); + multi_line_textinput.adjust_horizontal(3, Selection::NotSelected); + multi_line_textinput.handle_return(); + assert_eq!(multi_line_textinput.get_content().as_slice(), "abc\ndef"); +} + +#[test] +fn test_textinput_select_all() { + let mut textinput = TextInput::new(Lines::Multiple, "abc\nde\nf".into_string()); + assert_eq!(textinput.edit_point.line, 0); + assert_eq!(textinput.edit_point.index, 0); + + textinput.select_all(); + assert_eq!(textinput.edit_point.line, 2); + assert_eq!(textinput.edit_point.index, 1); +} + +#[test] +fn test_textinput_get_content() { + let single_line_textinput = TextInput::new(Lines::Single, "abcdefg".into_string()); + assert_eq!(single_line_textinput.get_content().as_slice(), "abcdefg"); + + let multi_line_textinput = TextInput::new(Lines::Multiple, "abc\nde\nf".into_string()); + assert_eq!(multi_line_textinput.get_content().as_slice(), "abc\nde\nf"); +} + +#[test] +fn test_textinput_set_content() { + let mut textinput = TextInput::new(Lines::Multiple, "abc\nde\nf".into_string()); + assert_eq!(textinput.get_content().as_slice(), "abc\nde\nf"); + + textinput.set_content("abc\nf".into_string()); + assert_eq!(textinput.get_content().as_slice(), "abc\nf"); + + assert_eq!(textinput.edit_point.line, 0); + assert_eq!(textinput.edit_point.index, 0); + textinput.adjust_horizontal(3, Selection::Selected); + assert_eq!(textinput.edit_point.line, 0); + assert_eq!(textinput.edit_point.index, 3); + textinput.set_content("de".into_string()); + assert_eq!(textinput.get_content().as_slice(), "de"); + assert_eq!(textinput.edit_point.line, 0); + // FIXME: https://github.com/servo/servo/issues/4622. + assert_eq!(textinput.edit_point.index, 1); +} + diff --git a/components/script/timers.rs b/components/script/timers.rs index c9a5495b30d..9c6ec47ffef 100644 --- a/components/script/timers.rs +++ b/components/script/timers.rs @@ -3,7 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use dom::bindings::cell::DOMRefCell; -use dom::bindings::callback::ExceptionHandling::ReportExceptions; +use dom::bindings::callback::ExceptionHandling::Report; use dom::bindings::codegen::Bindings::FunctionBinding::Function; use dom::bindings::js::JSRef; use dom::bindings::utils::Reflectable; @@ -189,7 +189,7 @@ impl TimerManager { // TODO: Must handle rooting of funval and args when movable GC is turned on match data.callback { TimerCallback::FunctionTimerCallback(function) => { - let _ = function.Call_(this, data.args, ReportExceptions); + let _ = function.Call_(this, data.args, Report); } TimerCallback::StringTimerCallback(code_str) => { this.evaluate_js_on_global_with_result(code_str.as_slice()); |