diff options
Diffstat (limited to 'components/script')
60 files changed, 1348 insertions, 289 deletions
diff --git a/components/script/Cargo.toml b/components/script/Cargo.toml index b7f7f37fbd4..f145e8f65b0 100644 --- a/components/script/Cargo.toml +++ b/components/script/Cargo.toml @@ -35,7 +35,7 @@ byteorder = "1.0" canvas_traits = {path = "../canvas_traits"} caseless = "0.1.0" cookie = "0.6" -cssparser = "0.12.1" +cssparser = "0.13" deny_public_fields = {path = "../deny_public_fields"} devtools_traits = {path = "../devtools_traits"} dom_struct = {path = "../dom_struct"} @@ -85,6 +85,7 @@ smallvec = "0.3" style = {path = "../style"} style_traits = {path = "../style_traits"} time = "0.1.12" +unicode-segmentation = "1.1.0" url = {version = "1.2", features = ["heap_size", "query_encoding"]} uuid = {version = "0.4", features = ["v4"]} xml5ever = {version = "0.5", features = ["unstable"]} diff --git a/components/script/dom/bindings/codegen/CodegenRust.py b/components/script/dom/bindings/codegen/CodegenRust.py index a54ea0c3c97..e7a0541c4da 100644 --- a/components/script/dom/bindings/codegen/CodegenRust.py +++ b/components/script/dom/bindings/codegen/CodegenRust.py @@ -1850,6 +1850,7 @@ class CGImports(CGWrapper): 'unused_imports', 'unused_variables', 'unused_assignments', + 'unused_mut', ] def componentTypes(type): diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs index 890074974d3..1bc9c165fb1 100644 --- a/components/script/dom/bindings/trace.rs +++ b/components/script/dom/bindings/trace.rs @@ -107,6 +107,7 @@ use time::Duration; use uuid::Uuid; use webrender_traits::{WebGLBufferId, WebGLError, WebGLFramebufferId, WebGLProgramId}; use webrender_traits::{WebGLRenderbufferId, WebGLShaderId, WebGLTextureId}; +use webvr_traits::WebVRGamepadHand; /// A trait to allow tracing (only) DOM objects. pub unsafe trait JSTraceable { @@ -381,6 +382,7 @@ unsafe_no_jsmanaged_fields!(WebGLRenderbufferId); unsafe_no_jsmanaged_fields!(WebGLShaderId); unsafe_no_jsmanaged_fields!(WebGLTextureId); unsafe_no_jsmanaged_fields!(MediaList); +unsafe_no_jsmanaged_fields!(WebVRGamepadHand); unsafe impl<'a> JSTraceable for &'a str { #[inline] @@ -726,7 +728,7 @@ impl<T: JSTraceable> RootableVec<T> { RootableVec { v: vec![], } - } + } } /// A vector of items that are rooted for the lifetime 'a. diff --git a/components/script/dom/blob.rs b/components/script/dom/blob.rs index cb61ced9e3a..c076282dcc1 100644 --- a/components/script/dom/blob.rs +++ b/components/script/dom/blob.rs @@ -353,12 +353,12 @@ pub fn blob_parts_to_bytes(blobparts: Vec<BlobOrString>) -> Result<Vec<u8>, ()> impl BlobMethods for Blob { // https://w3c.github.io/FileAPI/#dfn-size fn Size(&self) -> u64 { - match *self.blob_impl.borrow() { - BlobImpl::File(ref f) => f.size, - BlobImpl::Memory(ref v) => v.len() as u64, - BlobImpl::Sliced(ref parent, ref rel_pos) => - rel_pos.to_abs_range(parent.Size() as usize).len() as u64, - } + match *self.blob_impl.borrow() { + BlobImpl::File(ref f) => f.size, + BlobImpl::Memory(ref v) => v.len() as u64, + BlobImpl::Sliced(ref parent, ref rel_pos) => + rel_pos.to_abs_range(parent.Size() as usize).len() as u64, + } } // https://w3c.github.io/FileAPI/#dfn-type diff --git a/components/script/dom/canvasrenderingcontext2d.rs b/components/script/dom/canvasrenderingcontext2d.rs index 432dbe800aa..a5ec8d233b6 100644 --- a/components/script/dom/canvasrenderingcontext2d.rs +++ b/components/script/dom/canvasrenderingcontext2d.rs @@ -350,7 +350,7 @@ impl CanvasRenderingContext2D { dh); if !is_rect_valid(source_rect) || !is_rect_valid(dest_rect) { - return Err(Error::IndexSize); + return Ok(()); } let smoothing_enabled = self.state.borrow().image_smoothing_enabled; @@ -407,7 +407,7 @@ impl CanvasRenderingContext2D { dh); if !is_rect_valid(source_rect) || !is_rect_valid(dest_rect) { - return Err(Error::IndexSize); + return Ok(()); } let smoothing_enabled = self.state.borrow().image_smoothing_enabled; diff --git a/components/script/dom/css.rs b/components/script/dom/css.rs index 92c15a10067..72cdbfa5be6 100644 --- a/components/script/dom/css.rs +++ b/components/script/dom/css.rs @@ -9,7 +9,9 @@ use dom::bindings::reflector::Reflector; use dom::bindings::str::DOMString; use dom::window::Window; use dom_struct::dom_struct; -use style::parser::ParserContext; +use style::context::QuirksMode; +use style::parser::{LengthParsingMode, ParserContext}; +use style::stylesheets::CssRuleType; use style::supports::{Declaration, parse_condition_or_declaration}; #[dom_struct] @@ -29,7 +31,9 @@ impl CSS { pub fn Supports(win: &Window, property: DOMString, value: DOMString) -> bool { let decl = Declaration { prop: property.into(), val: value.into() }; let url = win.Document().url(); - let context = ParserContext::new_for_cssom(&url, win.css_error_reporter()); + let context = ParserContext::new_for_cssom(&url, win.css_error_reporter(), Some(CssRuleType::Supports), + LengthParsingMode::Default, + QuirksMode::NoQuirks); decl.eval(&context) } @@ -39,7 +43,9 @@ impl CSS { let cond = parse_condition_or_declaration(&mut input); if let Ok(cond) = cond { let url = win.Document().url(); - let context = ParserContext::new_for_cssom(&url, win.css_error_reporter()); + let context = ParserContext::new_for_cssom(&url, win.css_error_reporter(), Some(CssRuleType::Supports), + LengthParsingMode::Default, + QuirksMode::NoQuirks); cond.eval(&context) } else { false diff --git a/components/script/dom/csskeyframesrule.rs b/components/script/dom/csskeyframesrule.rs index 281ccd972a7..a3715e914ed 100644 --- a/components/script/dom/csskeyframesrule.rs +++ b/components/script/dom/csskeyframesrule.rs @@ -5,7 +5,7 @@ use cssparser::Parser; use dom::bindings::codegen::Bindings::CSSKeyframesRuleBinding; use dom::bindings::codegen::Bindings::CSSKeyframesRuleBinding::CSSKeyframesRuleMethods; -use dom::bindings::error::{Error, ErrorResult}; +use dom::bindings::error::ErrorResult; use dom::bindings::inheritance::Castable; use dom::bindings::js::{MutNullableJS, Root}; use dom::bindings::reflector::{DomObject, reflect_dom_object}; @@ -16,11 +16,11 @@ use dom::cssrulelist::{CSSRuleList, RulesSource}; use dom::cssstylesheet::CSSStyleSheet; use dom::window::Window; use dom_struct::dom_struct; -use servo_atoms::Atom; use std::sync::Arc; use style::keyframes::{Keyframe, KeyframeSelector}; use style::shared_lock::{Locked, ToCssWithGuard}; use style::stylesheets::KeyframesRule; +use style::values::KeyframesName; #[dom_struct] pub struct CSSKeyframesRule { @@ -107,23 +107,17 @@ impl CSSKeyframesRuleMethods for CSSKeyframesRule { // https://drafts.csswg.org/css-animations/#dom-csskeyframesrule-name fn Name(&self) -> DOMString { let guard = self.cssrule.shared_lock().read(); - DOMString::from(&*self.keyframesrule.read_with(&guard).name) + DOMString::from(&**self.keyframesrule.read_with(&guard).name.as_atom()) } // https://drafts.csswg.org/css-animations/#dom-csskeyframesrule-name fn SetName(&self, value: DOMString) -> ErrorResult { - // https://github.com/w3c/csswg-drafts/issues/801 - // Setting this property to a CSS-wide keyword or `none` will - // throw a Syntax Error. - match_ignore_ascii_case! { &value, - "initial" => return Err(Error::Syntax), - "inherit" => return Err(Error::Syntax), - "unset" => return Err(Error::Syntax), - "none" => return Err(Error::Syntax), - _ => () - } + // Spec deviation: https://github.com/w3c/csswg-drafts/issues/801 + // Setting this property to a CSS-wide keyword or `none` does not throw, + // it stores a value that serializes as a quoted string. + let name = KeyframesName::from_ident(value.into()); let mut guard = self.cssrule.shared_lock().write(); - self.keyframesrule.write_with(&mut guard).name = Atom::from(value); + self.keyframesrule.write_with(&mut guard).name = name; Ok(()) } } diff --git a/components/script/dom/cssmediarule.rs b/components/script/dom/cssmediarule.rs index 2a07471679e..45bf65b1adf 100644 --- a/components/script/dom/cssmediarule.rs +++ b/components/script/dom/cssmediarule.rs @@ -5,6 +5,7 @@ use cssparser::Parser; use dom::bindings::codegen::Bindings::CSSMediaRuleBinding; use dom::bindings::codegen::Bindings::CSSMediaRuleBinding::CSSMediaRuleMethods; +use dom::bindings::codegen::Bindings::WindowBinding::WindowBinding::WindowMethods; use dom::bindings::js::{MutNullableJS, Root}; use dom::bindings::reflector::{DomObject, reflect_dom_object}; use dom::bindings::str::DOMString; @@ -16,8 +17,9 @@ use dom::window::Window; use dom_struct::dom_struct; use std::sync::Arc; use style::media_queries::parse_media_query_list; +use style::parser::{LengthParsingMode, ParserContext}; use style::shared_lock::{Locked, ToCssWithGuard}; -use style::stylesheets::MediaRule; +use style::stylesheets::{CssRuleType, MediaRule}; use style_traits::ToCss; #[dom_struct] @@ -68,7 +70,14 @@ impl CSSMediaRule { /// https://drafts.csswg.org/css-conditional-3/#the-cssmediarule-interface pub fn set_condition_text(&self, text: DOMString) { let mut input = Parser::new(&text); - let new_medialist = parse_media_query_list(&mut input); + let global = self.global(); + let win = global.as_window(); + let url = win.get_url(); + let quirks_mode = win.Document().quirks_mode(); + let context = ParserContext::new_for_cssom(&url, win.css_error_reporter(), Some(CssRuleType::Media), + LengthParsingMode::Default, + quirks_mode); + let new_medialist = parse_media_query_list(&context, &mut input); let mut guard = self.cssconditionrule.shared_lock().write(); // Clone an Arc because we can’t borrow `guard` twice at the same time. diff --git a/components/script/dom/cssrule.rs b/components/script/dom/cssrule.rs index 1a24d5839bf..6bfe057d6d9 100644 --- a/components/script/dom/cssrule.rs +++ b/components/script/dom/cssrule.rs @@ -78,6 +78,7 @@ impl CSSRule { StyleCssRule::Import(s) => Root::upcast(CSSImportRule::new(window, parent_stylesheet, s)), StyleCssRule::Style(s) => Root::upcast(CSSStyleRule::new(window, parent_stylesheet, s)), StyleCssRule::FontFace(s) => Root::upcast(CSSFontFaceRule::new(window, parent_stylesheet, s)), + StyleCssRule::CounterStyle(_) => unimplemented!(), StyleCssRule::Keyframes(s) => Root::upcast(CSSKeyframesRule::new(window, parent_stylesheet, s)), StyleCssRule::Media(s) => Root::upcast(CSSMediaRule::new(window, parent_stylesheet, s)), StyleCssRule::Namespace(s) => Root::upcast(CSSNamespaceRule::new(window, parent_stylesheet, s)), diff --git a/components/script/dom/cssrulelist.rs b/components/script/dom/cssrulelist.rs index 4587659dfec..67eac2beda2 100644 --- a/components/script/dom/cssrulelist.rs +++ b/components/script/dom/cssrulelist.rs @@ -15,7 +15,7 @@ use dom::window::Window; use dom_struct::dom_struct; use std::sync::Arc; use style::shared_lock::Locked; -use style::stylesheets::{CssRules, KeyframesRule, RulesMutateError}; +use style::stylesheets::{CssRules, CssRulesHelpers, KeyframesRule, RulesMutateError}; #[allow(unsafe_code)] unsafe_no_jsmanaged_fields!(RulesSource); @@ -90,15 +90,13 @@ impl CSSRuleList { let index = idx as usize; let parent_stylesheet = self.parent_stylesheet.style_stylesheet(); - let new_rule = { - let mut guard = parent_stylesheet.shared_lock.write(); - // FIXME We should probably pass in a proper StylesheetLoader. - // See servo/servo#16240 - css_rules.write_with(&mut guard).insert_rule(rule, parent_stylesheet, - index, nested, None)? - // Drop `guard` here, - // CSSRule::new_specific re-acquires the lock for @support and @media. - }; + let new_rule = + css_rules.insert_rule(&parent_stylesheet.shared_lock, + rule, + parent_stylesheet, + index, + nested, + None)?; let parent_stylesheet = &*self.parent_stylesheet; let dom_rule = CSSRule::new_specific(&window, parent_stylesheet, new_rule); diff --git a/components/script/dom/cssstyledeclaration.rs b/components/script/dom/cssstyledeclaration.rs index 25b99479b7a..d0996f04567 100644 --- a/components/script/dom/cssstyledeclaration.rs +++ b/components/script/dom/cssstyledeclaration.rs @@ -18,6 +18,7 @@ use servo_url::ServoUrl; use std::ascii::AsciiExt; use std::sync::Arc; use style::attr::AttrValue; +use style::parser::LengthParsingMode; use style::properties::{Importance, PropertyDeclarationBlock, PropertyId, LonghandId, ShorthandId}; use style::properties::{parse_one_declaration, parse_style_attribute}; use style::selector_parser::PseudoElement; @@ -255,9 +256,12 @@ impl CSSStyleDeclaration { // Step 6 let window = self.owner.window(); + let quirks_mode = window.Document().quirks_mode(); let result = parse_one_declaration(id, &value, &self.owner.base_url(), - window.css_error_reporter()); + window.css_error_reporter(), + LengthParsingMode::Default, + quirks_mode); // Step 7 let parsed = match result { @@ -433,11 +437,13 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration { return Err(Error::NoModificationAllowed); } + let quirks_mode = window.Document().quirks_mode(); self.owner.mutate_associated_block(|mut pdb, mut _changed| { // Step 3 *pdb = parse_style_attribute(&value, &self.owner.base_url(), - window.css_error_reporter()); + window.css_error_reporter(), + quirks_mode); }); Ok(()) diff --git a/components/script/dom/csssupportsrule.rs b/components/script/dom/csssupportsrule.rs index 7ba3a24d038..86aedf10864 100644 --- a/components/script/dom/csssupportsrule.rs +++ b/components/script/dom/csssupportsrule.rs @@ -14,9 +14,9 @@ use dom::cssstylesheet::CSSStyleSheet; use dom::window::Window; use dom_struct::dom_struct; use std::sync::Arc; -use style::parser::ParserContext; +use style::parser::{LengthParsingMode, ParserContext}; use style::shared_lock::{Locked, ToCssWithGuard}; -use style::stylesheets::SupportsRule; +use style::stylesheets::{CssRuleType, SupportsRule}; use style::supports::SupportsCondition; use style_traits::ToCss; @@ -61,7 +61,10 @@ impl CSSSupportsRule { let global = self.global(); let win = global.as_window(); let url = win.Document().url(); - let context = ParserContext::new_for_cssom(&url, win.css_error_reporter()); + let quirks_mode = win.Document().quirks_mode(); + let context = ParserContext::new_for_cssom(&url, win.css_error_reporter(), Some(CssRuleType::Supports), + LengthParsingMode::Default, + quirks_mode); let enabled = cond.eval(&context); let mut guard = self.cssconditionrule.shared_lock().write(); let rule = self.supportsrule.write_with(&mut guard); diff --git a/components/script/dom/dissimilaroriginlocation.rs b/components/script/dom/dissimilaroriginlocation.rs index b4cd2d84e5d..7ea2e3efc3c 100644 --- a/components/script/dom/dissimilaroriginlocation.rs +++ b/components/script/dom/dissimilaroriginlocation.rs @@ -12,6 +12,7 @@ use dom::bindings::str::DOMString; use dom::bindings::str::USVString; use dom::dissimilaroriginwindow::DissimilarOriginWindow; use dom_struct::dom_struct; +use servo_url::MutableOrigin; /// Represents a dissimilar-origin `Location` that exists in another script thread. /// @@ -43,6 +44,11 @@ impl DissimilarOriginLocation { window, DissimilarOriginLocationBinding::Wrap) } + + #[allow(dead_code)] + pub fn origin(&self) -> &MutableOrigin { + self.window.origin() + } } impl DissimilarOriginLocationMethods for DissimilarOriginLocation { diff --git a/components/script/dom/dissimilaroriginwindow.rs b/components/script/dom/dissimilaroriginwindow.rs index 0f3cbd06df0..4188f51f0e5 100644 --- a/components/script/dom/dissimilaroriginwindow.rs +++ b/components/script/dom/dissimilaroriginwindow.rs @@ -19,6 +19,7 @@ use js::jsval::{JSVal, UndefinedValue}; use msg::constellation_msg::PipelineId; use script_traits::ScriptMsg as ConstellationMsg; use servo_url::ImmutableOrigin; +use servo_url::MutableOrigin; use servo_url::ServoUrl; /// Represents a dissimilar-origin `Window` that exists in another script thread. @@ -56,12 +57,18 @@ impl DissimilarOriginWindow { global_to_clone_from.constellation_chan().clone(), global_to_clone_from.scheduler_chan().clone(), global_to_clone_from.resource_threads().clone(), - timer_event_chan), + timer_event_chan, + global_to_clone_from.origin().clone()), browsing_context: JS::from_ref(browsing_context), location: MutNullableJS::new(None), }; unsafe { DissimilarOriginWindowBinding::Wrap(cx, win) } } + + #[allow(dead_code)] + pub fn origin(&self) -> &MutableOrigin { + self.globalscope.origin() + } } impl DissimilarOriginWindowMethods for DissimilarOriginWindow { diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index ee79d710563..06b8f7596cf 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -93,7 +93,6 @@ use dom_struct::dom_struct; use encoding::EncodingRef; use encoding::all::UTF_8; use euclid::point::Point2D; -use gfx_traits::ScrollRootId; use html5ever_atoms::{LocalName, QualName}; use hyper::header::{Header, SetCookie}; use hyper_serde::Serde; @@ -143,6 +142,7 @@ use time; use timers::OneshotTimerCallback; use url::Host; use url::percent_encoding::percent_decode; +use webrender_traits::ClipId; /// The number of times we are allowed to see spurious `requestAnimationFrame()` calls before /// falling back to fake ones. @@ -492,6 +492,7 @@ impl Document { // FIXME: This should check the dirty bit on the document, // not the document element. Needs some layout changes to make // that workable. + self.stylesheets_changed_since_reflow.get() || match self.GetDocumentElement() { Some(root) => { root.upcast::<Node>().has_dirty_descendants() || @@ -541,7 +542,7 @@ impl Document { self.quirks_mode.set(mode); if mode == QuirksMode::Quirks { - self.window.layout_chan().send(Msg::SetQuirksMode).unwrap(); + self.window.layout_chan().send(Msg::SetQuirksMode(mode)).unwrap(); } } @@ -699,9 +700,11 @@ impl Document { if let Some((x, y)) = point { // Step 3 + let global_scope = self.window.upcast::<GlobalScope>(); + let webrender_pipeline_id = global_scope.pipeline_id().to_webrender(); self.window.perform_a_scroll(x, y, - ScrollRootId::root(), + ClipId::root_scroll_node(webrender_pipeline_id), ScrollBehavior::Instant, target.r()); } @@ -2360,7 +2363,7 @@ impl Document { if attr.local_name() == &local_name!("width") || attr.local_name() == &local_name!("height") { entry.hint |= RESTYLE_SELF; - } + } let mut snapshot = entry.snapshot.as_mut().unwrap(); if snapshot.attrs.is_none() { @@ -2772,7 +2775,14 @@ impl DocumentMethods for Document { if self.is_html_document { local_name.make_ascii_lowercase(); } - let name = QualName::new(ns!(html), LocalName::from(local_name)); + + let ns = if self.is_html_document || self.content_type == "application/xhtml+xml" { + ns!(html) + } else { + ns!() + }; + + let name = QualName::new(ns, LocalName::from(local_name)); Ok(Element::create(name, None, self, ElementCreator::ScriptCreated)) } diff --git a/components/script/dom/dommatrixreadonly.rs b/components/script/dom/dommatrixreadonly.rs index 4f6331e0dd7..875e8e6f42c 100644 --- a/components/script/dom/dommatrixreadonly.rs +++ b/components/script/dom/dommatrixreadonly.rs @@ -37,7 +37,7 @@ impl DOMMatrixReadOnly { reflector_: Reflector::new(), matrix: DOMRefCell::new(matrix), is2D: Cell::new(is2D), - } + } } // https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-dommatrixreadonly diff --git a/components/script/dom/domrectlist.rs b/components/script/dom/domrectlist.rs deleted file mode 100644 index a7d99b99448..00000000000 --- a/components/script/dom/domrectlist.rs +++ /dev/null @@ -1,58 +0,0 @@ -/* 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 dom::bindings::codegen::Bindings::DOMRectListBinding; -use dom::bindings::codegen::Bindings::DOMRectListBinding::DOMRectListMethods; -use dom::bindings::js::{JS, Root}; -use dom::bindings::reflector::{Reflector, reflect_dom_object}; -use dom::domrect::DOMRect; -use dom::window::Window; -use dom_struct::dom_struct; - -#[dom_struct] -pub struct DOMRectList { - reflector_: Reflector, - rects: Vec<JS<DOMRect>>, -} - -impl DOMRectList { - fn new_inherited<T>(rects: T) -> DOMRectList - where T: Iterator<Item = Root<DOMRect>> - { - DOMRectList { - reflector_: Reflector::new(), - rects: rects.map(|r| JS::from_ref(&*r)).collect(), - } - } - - pub fn new<T>(window: &Window, rects: T) -> Root<DOMRectList> - where T: Iterator<Item = Root<DOMRect>> - { - reflect_dom_object(box DOMRectList::new_inherited(rects), - window, - DOMRectListBinding::Wrap) - } -} - -impl DOMRectListMethods for DOMRectList { - // https://drafts.fxtf.org/geometry/#dom-domrectlist-length - fn Length(&self) -> u32 { - self.rects.len() as u32 - } - - // https://drafts.fxtf.org/geometry/#dom-domrectlist-item - fn Item(&self, index: u32) -> Option<Root<DOMRect>> { - let rects = &self.rects; - if index < rects.len() as u32 { - Some(Root::from_ref(&*rects[index as usize])) - } else { - None - } - } - - // check-tidy: no specs after this line - fn IndexedGetter(&self, index: u32) -> Option<Root<DOMRect>> { - self.Item(index) - } -} diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs index 4b3a6010a9a..d6974fb9075 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -34,7 +34,6 @@ use dom::create::create_element; use dom::document::{Document, LayoutDocumentHelpers}; use dom::documentfragment::DocumentFragment; use dom::domrect::DOMRect; -use dom::domrectlist::DOMRectList; use dom::domtokenlist::DOMTokenList; use dom::event::Event; use dom::eventtarget::EventTarget; @@ -86,7 +85,7 @@ use net_traits::request::CorsSettings; use ref_filter_map::ref_filter_map; use script_layout_interface::message::ReflowQueryType; use script_thread::Runnable; -use selectors::matching::{ElementSelectorFlags, StyleRelations, matches}; +use selectors::matching::{ElementSelectorFlags, StyleRelations, matches_selector_list}; use selectors::matching::{HAS_EDGE_CHILD_SELECTOR, HAS_SLOW_SELECTOR, HAS_SLOW_SELECTOR_LATER_SIBLINGS}; use selectors::parser::{AttrSelector, NamespaceConstraint}; use servo_atoms::Atom; @@ -468,7 +467,7 @@ impl LayoutElementHelpers for LayoutJS<Element> { hints.push(from_declaration( shared_lock, PropertyDeclaration::FontFamily( - font_family::computed_value::T(vec![ + font_family::SpecifiedValue::Values(vec![ font_family::computed_value::FontFamily::from_atom( font_family)])))); } @@ -561,7 +560,7 @@ impl LayoutElementHelpers for LayoutJS<Element> { } LengthOrPercentageOrAuto::Length(length) => { let width_value = specified::LengthOrPercentageOrAuto::Length( - specified::NoCalcLength::Absolute(length)); + specified::NoCalcLength::Absolute(specified::AbsoluteLength::Px(length.to_f32_px()))); hints.push(from_declaration( shared_lock, PropertyDeclaration::Width(width_value))); @@ -590,7 +589,7 @@ impl LayoutElementHelpers for LayoutJS<Element> { } LengthOrPercentageOrAuto::Length(length) => { let height_value = specified::LengthOrPercentageOrAuto::Length( - specified::NoCalcLength::Absolute(length)); + specified::NoCalcLength::Absolute(specified::AbsoluteLength::Px(length.to_f32_px()))); hints.push(from_declaration( shared_lock, PropertyDeclaration::Height(height_value))); @@ -649,16 +648,16 @@ impl LayoutElementHelpers for LayoutJS<Element> { let width_value = specified::BorderWidth::from_length(specified::Length::from_px(border as f32)); hints.push(from_declaration( shared_lock, - PropertyDeclaration::BorderTopWidth(Box::new(width_value.clone())))); + PropertyDeclaration::BorderTopWidth(width_value.clone()))); hints.push(from_declaration( shared_lock, - PropertyDeclaration::BorderLeftWidth(Box::new(width_value.clone())))); + PropertyDeclaration::BorderLeftWidth(width_value.clone()))); hints.push(from_declaration( shared_lock, - PropertyDeclaration::BorderBottomWidth(Box::new(width_value.clone())))); + PropertyDeclaration::BorderBottomWidth(width_value.clone()))); hints.push(from_declaration( shared_lock, - PropertyDeclaration::BorderRightWidth(Box::new(width_value)))); + PropertyDeclaration::BorderRightWidth(width_value))); } } @@ -780,7 +779,7 @@ impl LayoutElementHelpers for LayoutJS<Element> { #[inline] #[allow(unsafe_code)] fn insert_selector_flags(&self, flags: ElementSelectorFlags) { - debug_assert!(thread_state::get() == thread_state::LAYOUT); + debug_assert!(thread_state::get().is_layout()); unsafe { let f = &(*self.unsafe_get()).selector_flags; f.set(f.get() | flags); @@ -1632,17 +1631,16 @@ impl ElementMethods for Element { } // https://drafts.csswg.org/cssom-view/#dom-element-getclientrects - fn GetClientRects(&self) -> Root<DOMRectList> { + fn GetClientRects(&self) -> Vec<Root<DOMRect>> { let win = window_from_node(self); let raw_rects = self.upcast::<Node>().content_boxes(); - let rects = raw_rects.iter().map(|rect| { + raw_rects.iter().map(|rect| { DOMRect::new(win.upcast(), rect.origin.x.to_f64_px(), rect.origin.y.to_f64_px(), rect.size.width.to_f64_px(), rect.size.height.to_f64_px()) - }); - DOMRectList::new(&win, rects) + }).collect() } // https://drafts.csswg.org/cssom-view/#dom-element-getboundingclientrect @@ -2050,7 +2048,7 @@ impl ElementMethods for Element { match SelectorParser::parse_author_origin_no_namespace(&selectors) { Err(()) => Err(Error::Syntax), Ok(selectors) => { - Ok(matches(&selectors.0, &Root::from_ref(self), None)) + Ok(matches_selector_list(&selectors.0, &Root::from_ref(self), None)) } } } @@ -2068,7 +2066,7 @@ impl ElementMethods for Element { let root = self.upcast::<Node>(); for element in root.inclusive_ancestors() { if let Some(element) = Root::downcast::<Element>(element) { - if matches(&selectors.0, &element, None) + if matches_selector_list(&selectors.0, &element, None) { return Ok(Some(element)); } @@ -2198,7 +2196,8 @@ impl VirtualMethods for Element { Arc::new(doc.style_shared_lock().wrap(parse_style_attribute( &attr.value(), &doc.base_url(), - win.css_error_reporter()))) + win.css_error_reporter(), + doc.quirks_mode()))) }; Some(block) @@ -2599,7 +2598,7 @@ impl Element { self.has_attribute(&local_name!("href")) }, _ => false, - } + } } /// Please call this method *only* for real click events diff --git a/components/script/dom/gamepad.rs b/components/script/dom/gamepad.rs new file mode 100644 index 00000000000..f9f28fc8c51 --- /dev/null +++ b/components/script/dom/gamepad.rs @@ -0,0 +1,206 @@ +/* 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 core::nonzero::NonZero; +use dom::bindings::codegen::Bindings::GamepadBinding; +use dom::bindings::codegen::Bindings::GamepadBinding::GamepadMethods; +use dom::bindings::inheritance::Castable; +use dom::bindings::js::{JS, Root}; +use dom::bindings::num::Finite; +use dom::bindings::reflector::{DomObject, Reflector, reflect_dom_object}; +use dom::bindings::str::DOMString; +use dom::event::Event; +use dom::eventtarget::EventTarget; +use dom::gamepadbuttonlist::GamepadButtonList; +use dom::gamepadevent::{GamepadEvent, GamepadEventType}; +use dom::globalscope::GlobalScope; +use dom::vrpose::VRPose; +use dom_struct::dom_struct; +use js::jsapi::{Heap, JSContext, JSObject}; +use js::typedarray::{Float64Array, CreateWith}; +use std::cell::Cell; +use std::ptr; +use webvr_traits::{WebVRGamepadData, WebVRGamepadHand, WebVRGamepadState}; + +#[dom_struct] +pub struct Gamepad { + reflector_: Reflector, + gamepad_id: u32, + id: String, + index: Cell<i32>, + connected: Cell<bool>, + timestamp: Cell<f64>, + mapping_type: String, + axes: Heap<*mut JSObject>, + buttons: JS<GamepadButtonList>, + pose: Option<JS<VRPose>>, + #[ignore_heap_size_of = "Defined in rust-webvr"] + hand: WebVRGamepadHand, + display_id: u32 +} + +impl Gamepad { + fn new_inherited(gamepad_id: u32, + id: String, + index: i32, + connected: bool, + timestamp: f64, + mapping_type: String, + axes: *mut JSObject, + buttons: &GamepadButtonList, + pose: Option<&VRPose>, + hand: WebVRGamepadHand, + display_id: u32) -> Gamepad { + Self { + reflector_: Reflector::new(), + gamepad_id: gamepad_id, + id: id, + index: Cell::new(index), + connected: Cell::new(connected), + timestamp: Cell::new(timestamp), + mapping_type: mapping_type, + axes: Heap::new(axes), + buttons: JS::from_ref(buttons), + pose: pose.map(JS::from_ref), + hand: hand, + display_id: display_id + } + } + + #[allow(unsafe_code)] + pub fn new_from_vr(global: &GlobalScope, + index: i32, + data: &WebVRGamepadData, + state: &WebVRGamepadState) -> Root<Gamepad> { + let buttons = GamepadButtonList::new_from_vr(&global, &state.buttons); + let pose = VRPose::new(&global, &state.pose); + let cx = global.get_cx(); + rooted!(in (cx) let mut axes = ptr::null_mut()); + unsafe { + let _ = Float64Array::create(cx, + CreateWith::Slice(&state.axes), + axes.handle_mut()); + } + + reflect_dom_object(box Gamepad::new_inherited(state.gamepad_id, + data.name.clone(), + index, + state.connected, + state.timestamp, + "".into(), + axes.get(), + &buttons, + Some(&pose), + data.hand.clone(), + data.display_id), + global, + GamepadBinding::Wrap) + + } +} + +impl GamepadMethods for Gamepad { + // https://w3c.github.io/gamepad/#dom-gamepad-id + fn Id(&self) -> DOMString { + DOMString::from(self.id.clone()) + } + + // https://w3c.github.io/gamepad/#dom-gamepad-index + fn Index(&self) -> i32 { + self.index.get() + } + + // https://w3c.github.io/gamepad/#dom-gamepad-connected + fn Connected(&self) -> bool { + self.connected.get() + } + + // https://w3c.github.io/gamepad/#dom-gamepad-timestamp + fn Timestamp(&self) -> Finite<f64> { + Finite::wrap(self.timestamp.get()) + } + + // https://w3c.github.io/gamepad/#dom-gamepad-mapping + fn Mapping(&self) -> DOMString { + DOMString::from(self.mapping_type.clone()) + } + + #[allow(unsafe_code)] + // https://w3c.github.io/gamepad/#dom-gamepad-axes + unsafe fn Axes(&self, _cx: *mut JSContext) -> NonZero<*mut JSObject> { + NonZero::new(self.axes.get()) + } + + // https://w3c.github.io/gamepad/#dom-gamepad-buttons + fn Buttons(&self) -> Root<GamepadButtonList> { + Root::from_ref(&*self.buttons) + } + + // https://w3c.github.io/gamepad/extensions.html#gamepadhand-enum + fn Hand(&self) -> DOMString { + let value = match self.hand { + WebVRGamepadHand::Unknown => "", + WebVRGamepadHand::Left => "left", + WebVRGamepadHand::Right => "right" + }; + value.into() + } + + // https://w3c.github.io/gamepad/extensions.html#dom-gamepad-pose + fn GetPose(&self) -> Option<Root<VRPose>> { + self.pose.as_ref().map(|p| Root::from_ref(&**p)) + } + + // https://w3c.github.io/webvr/spec/1.1/#gamepad-getvrdisplays-attribute + fn DisplayId(&self) -> u32 { + self.display_id + } +} + +impl Gamepad { + #[allow(unsafe_code)] + pub fn update_from_vr(&self, state: &WebVRGamepadState) { + self.timestamp.set(state.timestamp); + unsafe { + let cx = self.global().get_cx(); + typedarray!(in(cx) let axes: Float64Array = self.axes.get()); + if let Ok(mut array) = axes { + array.update(&state.axes); + } + } + self.buttons.sync_from_vr(&state.buttons); + if let Some(ref pose) = self.pose { + pose.update(&state.pose); + } + self.update_connected(state.connected); + } + + pub fn gamepad_id(&self) -> u32 { + self.gamepad_id + } + + pub fn update_connected(&self, connected: bool) { + if self.connected.get() == connected { + return; + } + self.connected.set(connected); + + let event_type = if connected { + GamepadEventType::Connected + } else { + GamepadEventType::Disconnected + }; + + self.notify_event(event_type); + } + + pub fn update_index(&self, index: i32) { + self.index.set(index); + } + + pub fn notify_event(&self, event_type: GamepadEventType) { + let event = GamepadEvent::new_with_type(&self.global(), event_type, &self); + event.upcast::<Event>().fire(self.global().as_window().upcast::<EventTarget>()); + } +} diff --git a/components/script/dom/gamepadbutton.rs b/components/script/dom/gamepadbutton.rs new file mode 100644 index 00000000000..ff0c7271e5b --- /dev/null +++ b/components/script/dom/gamepadbutton.rs @@ -0,0 +1,61 @@ +/* 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 dom::bindings::codegen::Bindings::GamepadButtonBinding; +use dom::bindings::codegen::Bindings::GamepadButtonBinding::GamepadButtonMethods; +use dom::bindings::js::Root; +use dom::bindings::num::Finite; +use dom::bindings::reflector::{Reflector, reflect_dom_object}; +use dom::globalscope::GlobalScope; +use dom_struct::dom_struct; +use std::cell::Cell; + +#[dom_struct] +pub struct GamepadButton { + reflector_: Reflector, + pressed: Cell<bool>, + touched: Cell<bool>, + value: Cell<f64>, +} + +impl GamepadButton { + pub fn new_inherited(pressed: bool, touched: bool) -> GamepadButton { + Self { + reflector_: Reflector::new(), + pressed: Cell::new(pressed), + touched: Cell::new(touched), + value: Cell::new(0.0), + } + } + + pub fn new(global: &GlobalScope, pressed: bool, touched: bool) -> Root<GamepadButton> { + reflect_dom_object(box GamepadButton::new_inherited(pressed, touched), + global, + GamepadButtonBinding::Wrap) + } +} + +impl GamepadButtonMethods for GamepadButton { + // https://www.w3.org/TR/gamepad/#widl-GamepadButton-pressed + fn Pressed(&self) -> bool { + self.pressed.get() + } + + // https://www.w3.org/TR/gamepad/#widl-GamepadButton-touched + fn Touched(&self) -> bool { + self.touched.get() + } + + // https://www.w3.org/TR/gamepad/#widl-GamepadButton-value + fn Value(&self) -> Finite<f64> { + Finite::wrap(self.value.get()) + } +} + +impl GamepadButton { + pub fn update(&self, pressed: bool, touched: bool) { + self.pressed.set(pressed); + self.touched.set(touched); + } +} diff --git a/components/script/dom/gamepadbuttonlist.rs b/components/script/dom/gamepadbuttonlist.rs new file mode 100644 index 00000000000..5ac25504009 --- /dev/null +++ b/components/script/dom/gamepadbuttonlist.rs @@ -0,0 +1,63 @@ +/* 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 dom::bindings::codegen::Bindings::GamepadButtonListBinding; +use dom::bindings::codegen::Bindings::GamepadButtonListBinding::GamepadButtonListMethods; +use dom::bindings::js::{JS, Root, RootedReference}; +use dom::bindings::reflector::{Reflector, reflect_dom_object}; +use dom::gamepadbutton::GamepadButton; +use dom::globalscope::GlobalScope; +use dom_struct::dom_struct; +use webvr_traits::WebVRGamepadButton; + +// https://w3c.github.io/gamepad/#gamepadbutton-interface +#[dom_struct] +pub struct GamepadButtonList { + reflector_: Reflector, + list: Vec<JS<GamepadButton>> +} + +impl GamepadButtonList { + #[allow(unrooted_must_root)] + fn new_inherited(list: &[&GamepadButton]) -> GamepadButtonList { + GamepadButtonList { + reflector_: Reflector::new(), + list: list.iter().map(|button| JS::from_ref(*button)).collect(), + } + } + + pub fn new_from_vr(global: &GlobalScope, buttons: &[WebVRGamepadButton]) -> Root<GamepadButtonList> { + rooted_vec!(let list <- buttons.iter() + .map(|btn| GamepadButton::new(&global, btn.pressed, btn.touched))); + + reflect_dom_object(box GamepadButtonList::new_inherited(list.r()), + global, + GamepadButtonListBinding::Wrap) + } + + pub fn sync_from_vr(&self, vr_buttons: &[WebVRGamepadButton]) { + let mut index = 0; + for btn in vr_buttons { + self.list.get(index).as_ref().unwrap().update(btn.pressed, btn.touched); + index += 1; + } + } +} + +impl GamepadButtonListMethods for GamepadButtonList { + // https://w3c.github.io/gamepad/#dom-gamepad-buttons + fn Length(&self) -> u32 { + self.list.len() as u32 + } + + // https://w3c.github.io/gamepad/#dom-gamepad-buttons + fn Item(&self, index: u32) -> Option<Root<GamepadButton>> { + self.list.get(index as usize).map(|button| Root::from_ref(&**button)) + } + + // https://w3c.github.io/gamepad/#dom-gamepad-buttons + fn IndexedGetter(&self, index: u32) -> Option<Root<GamepadButton>> { + self.Item(index) + } +} diff --git a/components/script/dom/gamepadevent.rs b/components/script/dom/gamepadevent.rs new file mode 100644 index 00000000000..f6690981a57 --- /dev/null +++ b/components/script/dom/gamepadevent.rs @@ -0,0 +1,92 @@ +/* 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 dom::bindings::codegen::Bindings::EventBinding::EventBinding::EventMethods; +use dom::bindings::codegen::Bindings::GamepadEventBinding; +use dom::bindings::codegen::Bindings::GamepadEventBinding::GamepadEventMethods; +use dom::bindings::error::Fallible; +use dom::bindings::inheritance::Castable; +use dom::bindings::js::{JS, Root}; +use dom::bindings::reflector::{DomObject, reflect_dom_object}; +use dom::bindings::str::DOMString; +use dom::event::Event; +use dom::gamepad::Gamepad; +use dom::globalscope::GlobalScope; +use dom::window::Window; +use dom_struct::dom_struct; +use servo_atoms::Atom; + +#[dom_struct] +pub struct GamepadEvent { + event: Event, + gamepad: JS<Gamepad>, +} + +pub enum GamepadEventType { + Connected, + Disconnected +} + +impl GamepadEvent { + fn new_inherited(gamepad: &Gamepad) -> GamepadEvent { + GamepadEvent { + event: Event::new_inherited(), + gamepad: JS::from_ref(gamepad), + } + } + + pub fn new(global: &GlobalScope, + type_: Atom, + bubbles: bool, + cancelable: bool, + gamepad: &Gamepad) + -> Root<GamepadEvent> { + let ev = reflect_dom_object(box GamepadEvent::new_inherited(&gamepad), + global, + GamepadEventBinding::Wrap); + { + let event = ev.upcast::<Event>(); + event.init_event(type_, bubbles, cancelable); + } + ev + } + + pub fn new_with_type(global: &GlobalScope, event_type: GamepadEventType, gamepad: &Gamepad) + -> Root<GamepadEvent> { + let name = match event_type { + GamepadEventType::Connected => "gamepadconnected", + GamepadEventType::Disconnected => "gamepaddisconnected" + }; + + GamepadEvent::new(&global, + name.into(), + false, + false, + &gamepad) + } + + // https://w3c.github.io/gamepad/#gamepadevent-interface + pub fn Constructor(window: &Window, + type_: DOMString, + init: &GamepadEventBinding::GamepadEventInit) + -> Fallible<Root<GamepadEvent>> { + Ok(GamepadEvent::new(&window.global(), + Atom::from(type_), + init.parent.bubbles, + init.parent.cancelable, + &init.gamepad)) + } +} + +impl GamepadEventMethods for GamepadEvent { + // https://w3c.github.io/gamepad/#gamepadevent-interface + fn Gamepad(&self) -> Root<Gamepad> { + Root::from_ref(&*self.gamepad) + } + + // https://dom.spec.whatwg.org/#dom-event-istrusted + fn IsTrusted(&self) -> bool { + self.event.IsTrusted() + } +} diff --git a/components/script/dom/gamepadlist.rs b/components/script/dom/gamepadlist.rs new file mode 100644 index 00000000000..dd5bdd757d9 --- /dev/null +++ b/components/script/dom/gamepadlist.rs @@ -0,0 +1,61 @@ +/* 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 dom::bindings::cell::DOMRefCell; +use dom::bindings::codegen::Bindings::GamepadListBinding; +use dom::bindings::codegen::Bindings::GamepadListBinding::GamepadListMethods; +use dom::bindings::js::{JS, Root}; +use dom::bindings::reflector::{Reflector, reflect_dom_object}; +use dom::gamepad::Gamepad; +use dom::globalscope::GlobalScope; +use dom_struct::dom_struct; + +// https://www.w3.org/TR/gamepad/ +#[dom_struct] +pub struct GamepadList { + reflector_: Reflector, + list: DOMRefCell<Vec<JS<Gamepad>>> +} + +impl GamepadList { + fn new_inherited(list: &[&Gamepad]) -> GamepadList { + GamepadList { + reflector_: Reflector::new(), + list: DOMRefCell::new(list.iter().map(|g| JS::from_ref(&**g)).collect()) + } + } + + pub fn new(global: &GlobalScope, list: &[&Gamepad]) -> Root<GamepadList> { + reflect_dom_object(box GamepadList::new_inherited(list), + global, + GamepadListBinding::Wrap) + } + + pub fn add_if_not_exists(&self, gamepads: &[Root<Gamepad>]) { + for gamepad in gamepads { + if !self.list.borrow().iter().any(|g| g.gamepad_id() == gamepad.gamepad_id()) { + self.list.borrow_mut().push(JS::from_ref(&*gamepad)); + // Ensure that the gamepad has the correct index + gamepad.update_index(self.list.borrow().len() as i32 - 1); + } + } + } +} + +impl GamepadListMethods for GamepadList { + // https://w3c.github.io/gamepad/#dom-navigator-getgamepads + fn Length(&self) -> u32 { + self.list.borrow().len() as u32 + } + + // https://w3c.github.io/gamepad/#dom-navigator-getgamepads + fn Item(&self, index: u32) -> Option<Root<Gamepad>> { + self.list.borrow().get(index as usize).map(|gamepad| Root::from_ref(&**gamepad)) + } + + // https://w3c.github.io/gamepad/#dom-navigator-getgamepads + fn IndexedGetter(&self, index: u32) -> Option<Root<Gamepad>> { + self.Item(index) + } +} diff --git a/components/script/dom/globalscope.rs b/components/script/dom/globalscope.rs index 73ba61e4557..931d1f576aa 100644 --- a/components/script/dom/globalscope.rs +++ b/components/script/dom/globalscope.rs @@ -38,7 +38,7 @@ use script_runtime::{CommonScriptMsg, ScriptChan, ScriptPort}; use script_thread::{MainThreadScriptChan, RunnableWrapper, ScriptThread}; use script_traits::{MsDuration, ScriptMsg as ConstellationMsg, TimerEvent}; use script_traits::{TimerEventId, TimerSchedulerMsg, TimerSource}; -use servo_url::ServoUrl; +use servo_url::{MutableOrigin, ServoUrl}; use std::cell::Cell; use std::collections::HashMap; use std::collections::hash_map::Entry; @@ -92,6 +92,9 @@ pub struct GlobalScope { resource_threads: ResourceThreads, timers: OneshotTimers, + + /// The origin of the globalscope + origin: MutableOrigin, } impl GlobalScope { @@ -103,7 +106,8 @@ impl GlobalScope { constellation_chan: IpcSender<ConstellationMsg>, scheduler_chan: IpcSender<TimerSchedulerMsg>, resource_threads: ResourceThreads, - timer_event_chan: IpcSender<TimerEvent>) + timer_event_chan: IpcSender<TimerEvent>, + origin: MutableOrigin) -> Self { GlobalScope { eventtarget: EventTarget::new_inherited(), @@ -120,6 +124,7 @@ impl GlobalScope { in_error_reporting_mode: Default::default(), resource_threads: resource_threads, timers: OneshotTimers::new(timer_event_chan, scheduler_chan), + origin: origin, } } @@ -238,6 +243,11 @@ impl GlobalScope { self.pipeline_id } + /// Get the origin for this global scope + pub fn origin(&self) -> &MutableOrigin { + &self.origin + } + /// Get the [base url](https://html.spec.whatwg.org/multipage/#api-base-url) /// for this global scope. pub fn api_base_url(&self) -> ServoUrl { diff --git a/components/script/dom/htmlimageelement.rs b/components/script/dom/htmlimageelement.rs index 7cebebade0a..389cd87b064 100644 --- a/components/script/dom/htmlimageelement.rs +++ b/components/script/dom/htmlimageelement.rs @@ -423,6 +423,11 @@ impl HTMLImageElement { }; let value = usemap_attr.value(); + + if value.len() == 0 || !value.is_char_boundary(1) { + return None + } + let (first, last) = value.split_at(1); if first != "#" || last.len() == 0 { diff --git a/components/script/dom/htmlinputelement.rs b/components/script/dom/htmlinputelement.rs index db26905b3b4..baff4983504 100755 --- a/components/script/dom/htmlinputelement.rs +++ b/components/script/dom/htmlinputelement.rs @@ -1117,7 +1117,7 @@ impl VirtualMethods for HTMLInputElement { translated_y ); if let Some(i) = index { - self.textinput.borrow_mut().edit_point.index = i as usize; + self.textinput.borrow_mut().set_edit_point_index(i as usize); // trigger redraw self.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage); event.PreventDefault(); diff --git a/components/script/dom/htmllinkelement.rs b/components/script/dom/htmllinkelement.rs index d29a94a11f0..bf36d28b29d 100644 --- a/components/script/dom/htmllinkelement.rs +++ b/components/script/dom/htmllinkelement.rs @@ -24,6 +24,7 @@ use dom::virtualmethods::VirtualMethods; use dom_struct::dom_struct; use html5ever_atoms::LocalName; use net_traits::ReferrerPolicy; +use script_layout_interface::message::Msg; use script_traits::{MozBrowserEvent, ScriptMsg as ConstellationMsg}; use std::ascii::AsciiExt; use std::borrow::ToOwned; @@ -32,8 +33,9 @@ use std::default::Default; use std::sync::Arc; use style::attr::AttrValue; use style::media_queries::parse_media_query_list; +use style::parser::{LengthParsingMode, ParserContext as CssParserContext}; use style::str::HTML_SPACE_CHARACTERS; -use style::stylesheets::Stylesheet; +use style::stylesheets::{CssRuleType, Stylesheet}; use stylesheet_loader::{StylesheetLoader, StylesheetContextSource, StylesheetOwner}; unsafe_no_jsmanaged_fields!(Stylesheet); @@ -96,8 +98,9 @@ impl HTMLLinkElement { } pub fn set_stylesheet(&self, s: Arc<Stylesheet>) { - assert!(self.stylesheet.borrow().is_none()); // Useful for catching timing issues. - *self.stylesheet.borrow_mut() = Some(s); + *self.stylesheet.borrow_mut() = Some(s.clone()); + window_from_node(self).layout_chan().send(Msg::AddStylesheet(s)).unwrap(); + document_from_node(self).invalidate_stylesheets(); } pub fn get_stylesheet(&self) -> Option<Arc<Stylesheet>> { @@ -255,7 +258,7 @@ impl HTMLLinkElement { } // Step 2. - let url = match document.base_url().join(href) { + let link_url = match document.base_url().join(href) { Ok(url) => url, Err(e) => { debug!("Parsing url {} failed: {}", href, e); @@ -276,7 +279,12 @@ impl HTMLLinkElement { }; let mut css_parser = CssParser::new(&mq_str); - let media = parse_media_query_list(&mut css_parser); + let win = document.window(); + let doc_url = document.url(); + let context = CssParserContext::new_for_cssom(&doc_url, win.css_error_reporter(), Some(CssRuleType::Media), + LengthParsingMode::Default, + document.quirks_mode()); + let media = parse_media_query_list(&context, &mut css_parser); let im_attribute = element.get_attribute(&ns!(), &local_name!("integrity")); let integrity_val = im_attribute.r().map(|a| a.value()); @@ -292,7 +300,7 @@ impl HTMLLinkElement { let loader = StylesheetLoader::for_element(self.upcast()); loader.load(StylesheetContextSource::LinkElement { media: Some(media), - }, url, cors_setting, integrity_metadata.to_owned()); + }, link_url, cors_setting, integrity_metadata.to_owned()); } fn handle_favicon_url(&self, rel: &str, href: &str, sizes: &Option<String>) { diff --git a/components/script/dom/htmlmetaelement.rs b/components/script/dom/htmlmetaelement.rs index 8613f4c8cb0..62d7215e3df 100644 --- a/components/script/dom/htmlmetaelement.rs +++ b/components/script/dom/htmlmetaelement.rs @@ -24,6 +24,7 @@ use std::ascii::AsciiExt; use std::sync::Arc; use std::sync::atomic::AtomicBool; use style::attr::AttrValue; +use style::media_queries::MediaList; use style::str::HTML_SPACE_CHARACTERS; use style::stylesheets::{Stylesheet, CssRule, CssRules, Origin}; use style::viewport::ViewportRule; @@ -107,11 +108,12 @@ impl HTMLMetaElement { shared_lock: shared_lock.clone(), url_data: window_from_node(self).get_url(), namespaces: Default::default(), - media: Arc::new(shared_lock.wrap(Default::default())), + media: Arc::new(shared_lock.wrap(MediaList::empty())), // Viewport constraints are always recomputed on resize; they don't need to // force all styles to be recomputed. dirty_on_viewport_size_change: AtomicBool::new(false), disabled: AtomicBool::new(false), + quirks_mode: document.quirks_mode(), })); let doc = document_from_node(self); doc.invalidate_stylesheets(); diff --git a/components/script/dom/htmlscriptelement.rs b/components/script/dom/htmlscriptelement.rs index f13c0224b1a..d6679c5168c 100644 --- a/components/script/dom/htmlscriptelement.rs +++ b/components/script/dom/htmlscriptelement.rs @@ -34,11 +34,17 @@ use net_traits::{FetchMetadata, FetchResponseListener, Metadata, NetworkError}; use net_traits::request::{CorsSettings, CredentialsMode, Destination, RequestInit, RequestMode, Type as RequestType}; use network_listener::{NetworkListener, PreInvoke}; use servo_atoms::Atom; +use servo_config::opts; use servo_url::ServoUrl; use std::ascii::AsciiExt; use std::cell::Cell; +use std::fs::File; +use std::io::{Read, Write}; +use std::path::PathBuf; +use std::process::{Command, Stdio}; use std::sync::{Arc, Mutex}; use style::str::{HTML_SPACE_CHARACTERS, StaticStringVec}; +use uuid::Uuid; #[dom_struct] pub struct HTMLScriptElement { @@ -450,6 +456,49 @@ impl HTMLScriptElement { } } + fn unminify_js(&self, script: &mut ClassicScript) { + if !opts::get().unminify_js { + return; + } + + match Command::new("js-beautify") + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .spawn() { + Err(_) => { + warn!("Failed to execute js-beautify. Will store unmodified script"); + }, + Ok(process) => { + let mut script_content = String::from(script.text.clone()); + let _ = process.stdin.unwrap().write_all(script_content.as_bytes()); + script_content.clear(); + let _ = process.stdout.unwrap().read_to_string(&mut script_content); + + script.text = DOMString::from(script_content); + }, + } + + let path = PathBuf::from(window_from_node(self).unminified_js_dir().unwrap()); + let path = if script.external { + // External script. + let path_parts = script.url.path_segments().unwrap(); + match path_parts.last() { + Some(script_name) => path.join(script_name), + None => path.join(Uuid::new_v4().to_string()), + } + } else { + // Inline script. + path.join(Uuid::new_v4().to_string()) + }; + + debug!("script will be stored in {:?}", path); + + match File::create(&path) { + Ok(mut file) => file.write_all(script.text.as_bytes()).unwrap(), + Err(why) => warn!("Could not store script {:?}", why), + } + } + /// https://html.spec.whatwg.org/multipage/#execute-the-script-block pub fn execute(&self, result: Result<ClassicScript, NetworkError>) { // Step 1. @@ -458,7 +507,7 @@ impl HTMLScriptElement { return; } - let script = match result { + let mut script = match result { // Step 2. Err(e) => { warn!("error loading script {:?}", e); @@ -469,6 +518,8 @@ impl HTMLScriptElement { Ok(script) => script, }; + self.unminify_js(&mut script); + // Step 3. let neutralized_doc = if script.external { debug!("loading external script, url = {}", script.url); diff --git a/components/script/dom/htmlstyleelement.rs b/components/script/dom/htmlstyleelement.rs index c3ef5767f2b..8905c3baa65 100644 --- a/components/script/dom/htmlstyleelement.rs +++ b/components/script/dom/htmlstyleelement.rs @@ -25,7 +25,8 @@ use script_layout_interface::message::Msg; use std::cell::Cell; use std::sync::Arc; use style::media_queries::parse_media_query_list; -use style::stylesheets::{Stylesheet, Origin}; +use style::parser::{LengthParsingMode, ParserContext as CssParserContext}; +use style::stylesheets::{CssRuleType, Stylesheet, Origin}; use stylesheet_loader::{StylesheetLoader, StylesheetOwner}; #[dom_struct] @@ -39,6 +40,7 @@ pub struct HTMLStyleElement { in_stack_of_open_elements: Cell<bool>, pending_loads: Cell<u32>, any_failed_load: Cell<bool>, + line_number: u64, } impl HTMLStyleElement { @@ -54,6 +56,7 @@ impl HTMLStyleElement { in_stack_of_open_elements: Cell::new(creator.is_parser_created()), pending_loads: Cell::new(0), any_failed_load: Cell::new(false), + line_number: creator.return_line_number(), } } @@ -73,7 +76,7 @@ impl HTMLStyleElement { assert!(node.is_in_doc()); let win = window_from_node(node); - let url = win.get_url(); + let doc = document_from_node(self); let mq_attribute = element.get_attribute(&ns!(), &local_name!("media")); let mq_str = match mq_attribute { @@ -82,12 +85,21 @@ impl HTMLStyleElement { }; let data = node.GetTextContent().expect("Element.textContent must be a string"); - let mq = parse_media_query_list(&mut CssParser::new(&mq_str)); + let url = win.get_url(); + let context = CssParserContext::new_for_cssom(&url, + win.css_error_reporter(), + Some(CssRuleType::Media), + LengthParsingMode::Default, + doc.quirks_mode()); let shared_lock = node.owner_doc().style_shared_lock().clone(); + let mq = Arc::new(shared_lock.wrap( + parse_media_query_list(&context, &mut CssParser::new(&mq_str)))); let loader = StylesheetLoader::for_element(self.upcast()); - let sheet = Stylesheet::from_str(&data, url, Origin::Author, mq, + let sheet = Stylesheet::from_str(&data, win.get_url(), Origin::Author, mq, shared_lock, Some(&loader), - win.css_error_reporter()); + win.css_error_reporter(), + doc.quirks_mode(), + self.line_number); let sheet = Arc::new(sheet); @@ -98,7 +110,6 @@ impl HTMLStyleElement { win.layout_chan().send(Msg::AddStylesheet(sheet.clone())).unwrap(); *self.stylesheet.borrow_mut() = Some(sheet); - let doc = document_from_node(self); doc.invalidate_stylesheets(); } diff --git a/components/script/dom/location.rs b/components/script/dom/location.rs index 399e3748722..cac2322be82 100644 --- a/components/script/dom/location.rs +++ b/components/script/dom/location.rs @@ -13,7 +13,7 @@ use dom::globalscope::GlobalScope; use dom::urlhelper::UrlHelper; use dom::window::Window; use dom_struct::dom_struct; -use servo_url::ServoUrl; +use servo_url::{MutableOrigin, ServoUrl}; #[dom_struct] pub struct Location { @@ -60,6 +60,11 @@ impl Location { pub fn reload_without_origin_check(&self) { self.window.load_url(self.get_url(), true, true, None); } + + #[allow(dead_code)] + pub fn origin(&self) -> &MutableOrigin { + self.window.origin() + } } impl LocationMethods for Location { diff --git a/components/script/dom/medialist.rs b/components/script/dom/medialist.rs index b403200983e..f2a2299d169 100644 --- a/components/script/dom/medialist.rs +++ b/components/script/dom/medialist.rs @@ -2,12 +2,12 @@ * 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 core::default::Default; use cssparser::Parser; use dom::bindings::codegen::Bindings::MediaListBinding; use dom::bindings::codegen::Bindings::MediaListBinding::MediaListMethods; +use dom::bindings::codegen::Bindings::WindowBinding::WindowBinding::WindowMethods; use dom::bindings::js::{JS, Root}; -use dom::bindings::reflector::{Reflector, reflect_dom_object}; +use dom::bindings::reflector::{DomObject, Reflector, reflect_dom_object}; use dom::bindings::str::DOMString; use dom::cssstylesheet::CSSStyleSheet; use dom::window::Window; @@ -15,7 +15,9 @@ use dom_struct::dom_struct; use std::sync::Arc; use style::media_queries::{MediaQuery, parse_media_query_list}; use style::media_queries::MediaList as StyleMediaList; +use style::parser::{LengthParsingMode, ParserContext}; use style::shared_lock::{SharedRwLock, Locked}; +use style::stylesheets::CssRuleType; use style_traits::ToCss; #[dom_struct] @@ -65,12 +67,19 @@ impl MediaListMethods for MediaList { // Step 2 if value.is_empty() { // Step 1 - *media_queries = StyleMediaList::default(); + *media_queries = StyleMediaList::empty(); return; } // Step 3 let mut parser = Parser::new(&value); - *media_queries = parse_media_query_list(&mut parser); + let global = self.global(); + let win = global.as_window(); + let url = win.get_url(); + let quirks_mode = win.Document().quirks_mode(); + let context = ParserContext::new_for_cssom(&url, win.css_error_reporter(), Some(CssRuleType::Media), + LengthParsingMode::Default, + quirks_mode); + *media_queries = parse_media_query_list(&context, &mut parser); } // https://drafts.csswg.org/cssom/#dom-medialist-length @@ -99,7 +108,14 @@ impl MediaListMethods for MediaList { fn AppendMedium(&self, medium: DOMString) { // Step 1 let mut parser = Parser::new(&medium); - let m = MediaQuery::parse(&mut parser); + let global = self.global(); + let win = global.as_window(); + let url = win.get_url(); + let quirks_mode = win.Document().quirks_mode(); + let context = ParserContext::new_for_cssom(&url, win.css_error_reporter(), Some(CssRuleType::Media), + LengthParsingMode::Default, + quirks_mode); + let m = MediaQuery::parse(&context, &mut parser); // Step 2 if let Err(_) = m { return; @@ -120,7 +136,14 @@ impl MediaListMethods for MediaList { fn DeleteMedium(&self, medium: DOMString) { // Step 1 let mut parser = Parser::new(&medium); - let m = MediaQuery::parse(&mut parser); + let global = self.global(); + let win = global.as_window(); + let url = win.get_url(); + let quirks_mode = win.Document().quirks_mode(); + let context = ParserContext::new_for_cssom(&url, win.css_error_reporter(), Some(CssRuleType::Media), + LengthParsingMode::Default, + quirks_mode); + let m = MediaQuery::parse(&context, &mut parser); // Step 2 if let Err(_) = m { return; diff --git a/components/script/dom/mediaquerylist.rs b/components/script/dom/mediaquerylist.rs index dafc7602794..b25e041f4ef 100644 --- a/components/script/dom/mediaquerylist.rs +++ b/components/script/dom/mediaquerylist.rs @@ -77,7 +77,7 @@ impl MediaQueryList { if let Some(window_size) = self.document.window().window_size() { let viewport_size = window_size.initial_viewport; let device = Device::new(MediaType::Screen, viewport_size); - self.media_query_list.evaluate(&device) + self.media_query_list.evaluate(&device, self.document.quirks_mode()) } else { false } diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs index 2cfcfc894ea..70d1f30bd4a 100644 --- a/components/script/dom/mod.rs +++ b/components/script/dom/mod.rs @@ -272,7 +272,6 @@ pub mod dompoint; pub mod dompointreadonly; pub mod domquad; pub mod domrect; -pub mod domrectlist; pub mod domrectreadonly; pub mod domstringmap; pub mod domtokenlist; @@ -290,6 +289,11 @@ pub mod filereadersync; pub mod focusevent; pub mod forcetouchevent; pub mod formdata; +pub mod gamepad; +pub mod gamepadbutton; +pub mod gamepadbuttonlist; +pub mod gamepadevent; +pub mod gamepadlist; pub mod globalscope; pub mod hashchangeevent; pub mod headers; diff --git a/components/script/dom/navigator.rs b/components/script/dom/navigator.rs index 118f53ac49b..32dd2900e17 100644 --- a/components/script/dom/navigator.rs +++ b/components/script/dom/navigator.rs @@ -8,6 +8,7 @@ use dom::bindings::js::{MutNullableJS, Root}; use dom::bindings::reflector::{Reflector, DomObject, reflect_dom_object}; use dom::bindings::str::DOMString; use dom::bluetooth::Bluetooth; +use dom::gamepadlist::GamepadList; use dom::mimetypearray::MimeTypeArray; use dom::navigatorinfo; use dom::permissions::Permissions; @@ -16,7 +17,6 @@ use dom::serviceworkercontainer::ServiceWorkerContainer; use dom::vr::VR; use dom::window::Window; use dom_struct::dom_struct; -use script_traits::WebVREventMsg; #[dom_struct] pub struct Navigator { @@ -26,6 +26,7 @@ pub struct Navigator { mime_types: MutNullableJS<MimeTypeArray>, service_worker: MutNullableJS<ServiceWorkerContainer>, vr: MutNullableJS<VR>, + gamepads: MutNullableJS<GamepadList>, permissions: MutNullableJS<Permissions>, } @@ -38,6 +39,7 @@ impl Navigator { mime_types: Default::default(), service_worker: Default::default(), vr: Default::default(), + gamepads: Default::default(), permissions: Default::default(), } } @@ -128,15 +130,19 @@ impl NavigatorMethods for Navigator { self.vr.or_init(|| VR::new(&self.global())) } + // https://www.w3.org/TR/gamepad/#navigator-interface-extension + fn GetGamepads(&self) -> Root<GamepadList> { + let root = self.gamepads.or_init(|| { + GamepadList::new(&self.global(), &[]) + }); + + let vr_gamepads = self.Vr().get_gamepads(); + root.add_if_not_exists(&vr_gamepads); + // TODO: Add not VR related gamepads + root + } // https://w3c.github.io/permissions/#navigator-and-workernavigator-extension fn Permissions(&self) -> Root<Permissions> { self.permissions.or_init(|| Permissions::new(&self.global())) } } - -impl Navigator { - pub fn handle_webvr_event(&self, event: WebVREventMsg) { - self.vr.get().expect("Shouldn't arrive here with an empty VR instance") - .handle_webvr_event(event); - } -} diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs index e21dfa2bee0..0384c4ed19d 100644 --- a/components/script/dom/node.rs +++ b/components/script/dom/node.rs @@ -68,7 +68,7 @@ use script_layout_interface::{LayoutElementType, LayoutNodeType, TrustedNodeAddr use script_layout_interface::message::Msg; use script_traits::DocumentActivity; use script_traits::UntrustedNodeAddress; -use selectors::matching::matches; +use selectors::matching::matches_selector_list; use selectors::parser::SelectorList; use servo_url::ServoUrl; use std::borrow::ToOwned; @@ -332,7 +332,7 @@ impl<'a> Iterator for QuerySelectorIterator { // (instead of passing `None`)? Probably. self.iterator.by_ref().filter_map(|node| { if let Some(element) = Root::downcast(node) { - if matches(selectors, &element, None) { + if matches_selector_list(selectors, &element, None) { return Some(Root::upcast(element)); } } @@ -695,7 +695,7 @@ impl Node { // Step 3. Ok(selectors) => { Ok(self.traverse_preorder().filter_map(Root::downcast).find(|element| { - matches(&selectors.0, element, None) + matches_selector_list(&selectors.0, element, None) })) } } @@ -2383,7 +2383,7 @@ impl NodeMethods for Node { // Step 2. Node::namespace_to_string(Node::locate_namespace(self, prefix)) - } + } // https://dom.spec.whatwg.org/#dom-node-isdefaultnamespace fn IsDefaultNamespace(&self, namespace: Option<DOMString>) -> bool { diff --git a/components/script/dom/nodelist.rs b/components/script/dom/nodelist.rs index 5c0983ccaa3..fae181f7d81 100644 --- a/components/script/dom/nodelist.rs +++ b/components/script/dom/nodelist.rs @@ -223,9 +223,9 @@ impl ChildrenList { // by ChildrenMutation::replace(). unreachable!() }, - (_, &[node, ..], _) => node, - (_, &[], Some(next)) => next, - (Some(prev), &[], None) => { + (_, added, _) if !added.is_empty() => added[0], + (_, _, Some(next)) => next, + (Some(prev), _, None) => { list.last_index.set(index - 1u32); prev }, diff --git a/components/script/dom/treewalker.rs b/components/script/dom/treewalker.rs index 24925b9f0d9..2409d65af4f 100644 --- a/components/script/dom/treewalker.rs +++ b/components/script/dom/treewalker.rs @@ -457,7 +457,7 @@ impl<'a> Iterator for &'a TreeWalker { // will probably be using a native Rust filter, // which cannot produce an Err result. unreachable!() - } + } } } diff --git a/components/script/dom/vr.rs b/components/script/dom/vr.rs index aea8230ac0e..9497405e8c9 100644 --- a/components/script/dom/vr.rs +++ b/components/script/dom/vr.rs @@ -5,12 +5,15 @@ use dom::bindings::cell::DOMRefCell; use dom::bindings::codegen::Bindings::VRBinding; use dom::bindings::codegen::Bindings::VRBinding::VRMethods; +use dom::bindings::codegen::Bindings::VRDisplayBinding::VRDisplayMethods; use dom::bindings::error::Error; use dom::bindings::inheritance::Castable; use dom::bindings::js::{JS, Root}; use dom::bindings::reflector::{DomObject, reflect_dom_object}; use dom::event::Event; use dom::eventtarget::EventTarget; +use dom::gamepad::Gamepad; +use dom::gamepadevent::GamepadEventType; use dom::globalscope::GlobalScope; use dom::promise::Promise; use dom::vrdisplay::VRDisplay; @@ -18,22 +21,23 @@ use dom::vrdisplayevent::VRDisplayEvent; use dom_struct::dom_struct; use ipc_channel::ipc; use ipc_channel::ipc::IpcSender; -use script_traits::WebVREventMsg; use std::rc::Rc; -use webvr_traits::WebVRMsg; -use webvr_traits::webvr; +use webvr_traits::{WebVRDisplayData, WebVRDisplayEvent, WebVREvent, WebVRMsg}; +use webvr_traits::{WebVRGamepadData, WebVRGamepadEvent, WebVRGamepadState}; #[dom_struct] pub struct VR { eventtarget: EventTarget, - displays: DOMRefCell<Vec<JS<VRDisplay>>> + displays: DOMRefCell<Vec<JS<VRDisplay>>>, + gamepads: DOMRefCell<Vec<JS<Gamepad>>> } impl VR { fn new_inherited() -> VR { VR { eventtarget: EventTarget::new_inherited(), - displays: DOMRefCell::new(Vec::new()) + displays: DOMRefCell::new(Vec::new()), + gamepads: DOMRefCell::new(Vec::new()), } } @@ -95,10 +99,10 @@ impl VR { self.global().as_window().webvr_thread() } - fn find_display(&self, display_id: u64) -> Option<Root<VRDisplay>> { + fn find_display(&self, display_id: u32) -> Option<Root<VRDisplay>> { self.displays.borrow() .iter() - .find(|d| d.get_display_id() == display_id) + .find(|d| d.DisplayId() == display_id) .map(|d| Root::from_ref(&**d)) } @@ -116,7 +120,7 @@ impl VR { } } - fn sync_display(&self, display: &webvr::VRDisplayData) -> Root<VRDisplay> { + fn sync_display(&self, display: &WebVRDisplayData) -> Root<VRDisplay> { if let Some(existing) = self.find_display(display.display_id) { existing.update_display(&display); existing @@ -127,35 +131,121 @@ impl VR { } } - pub fn handle_webvr_event(&self, event: WebVREventMsg) { - let WebVREventMsg::DisplayEvent(event) = event; - match &event { - &webvr::VRDisplayEvent::Connect(ref display) => { + fn handle_display_event(&self, event: WebVRDisplayEvent) { + match event { + WebVRDisplayEvent::Connect(ref display) => { let display = self.sync_display(&display); display.handle_webvr_event(&event); - self.notify_event(&display, &event); + self.notify_display_event(&display, &event); }, - &webvr::VRDisplayEvent::Disconnect(id) => { + WebVRDisplayEvent::Disconnect(id) => { if let Some(display) = self.find_display(id) { display.handle_webvr_event(&event); - self.notify_event(&display, &event); + self.notify_display_event(&display, &event); } }, - &webvr::VRDisplayEvent::Activate(ref display, _) | - &webvr::VRDisplayEvent::Deactivate(ref display, _) | - &webvr::VRDisplayEvent::Blur(ref display) | - &webvr::VRDisplayEvent::Focus(ref display) | - &webvr::VRDisplayEvent::PresentChange(ref display, _) | - &webvr::VRDisplayEvent::Change(ref display) => { + WebVRDisplayEvent::Activate(ref display, _) | + WebVRDisplayEvent::Deactivate(ref display, _) | + WebVRDisplayEvent::Blur(ref display) | + WebVRDisplayEvent::Focus(ref display) | + WebVRDisplayEvent::PresentChange(ref display, _) | + WebVRDisplayEvent::Change(ref display) => { let display = self.sync_display(&display); display.handle_webvr_event(&event); } }; } - fn notify_event(&self, display: &VRDisplay, event: &webvr::VRDisplayEvent) { + fn handle_gamepad_event(&self, event: WebVRGamepadEvent) { + match event { + WebVRGamepadEvent::Connect(data, state) => { + if let Some(gamepad) = self.find_gamepad(state.gamepad_id) { + gamepad.update_from_vr(&state); + } else { + // new gamepad + self.sync_gamepad(Some(data), &state); + } + }, + WebVRGamepadEvent::Disconnect(id) => { + if let Some(gamepad) = self.find_gamepad(id) { + gamepad.update_connected(false); + } + } + }; + } + + pub fn handle_webvr_event(&self, event: WebVREvent) { + match event { + WebVREvent::Display(event) => { + self.handle_display_event(event); + }, + WebVREvent::Gamepad(event) => { + self.handle_gamepad_event(event); + } + }; + } + + pub fn handle_webvr_events(&self, events: Vec<WebVREvent>) { + for event in events { + self.handle_webvr_event(event); + } + } + + fn notify_display_event(&self, display: &VRDisplay, event: &WebVRDisplayEvent) { let event = VRDisplayEvent::new_from_webvr(&self.global(), &display, &event); event.upcast::<Event>().fire(self.upcast()); } } +// Gamepad +impl VR { + fn find_gamepad(&self, gamepad_id: u32) -> Option<Root<Gamepad>> { + self.gamepads.borrow() + .iter() + .find(|g| g.gamepad_id() == gamepad_id) + .map(|g| Root::from_ref(&**g)) + } + + fn sync_gamepad(&self, data: Option<WebVRGamepadData>, state: &WebVRGamepadState) { + if let Some(existing) = self.find_gamepad(state.gamepad_id) { + existing.update_from_vr(&state); + } else { + let index = self.gamepads.borrow().len(); + let data = data.unwrap_or_default(); + let root = Gamepad::new_from_vr(&self.global(), + index as i32, + &data, + &state); + self.gamepads.borrow_mut().push(JS::from_ref(&*root)); + if state.connected { + root.notify_event(GamepadEventType::Connected); + } + } + } + + // Gamepads are synced immediately in response to the API call. + // The current approach allows the to sample gamepad state multiple times per frame. This + // guarantees that the gamepads always have a valid state and can be very useful for + // motion capture or drawing applications. + pub fn get_gamepads(&self) -> Vec<Root<Gamepad>> { + if let Some(wevbr_sender) = self.webvr_thread() { + let (sender, receiver) = ipc::channel().unwrap(); + let synced_ids = self.gamepads.borrow().iter().map(|g| g.gamepad_id()).collect(); + wevbr_sender.send(WebVRMsg::GetGamepads(synced_ids, sender)).unwrap(); + match receiver.recv().unwrap() { + Ok(gamepads) => { + // Sync displays + for gamepad in gamepads { + self.sync_gamepad(gamepad.0, &gamepad.1); + } + }, + Err(_) => {} + } + } + + // We can add other not VR related gamepad providers here + self.gamepads.borrow().iter() + .map(|g| Root::from_ref(&**g)) + .collect() + } +} diff --git a/components/script/dom/vrdisplay.rs b/components/script/dom/vrdisplay.rs index c05276c2349..b9b65264c44 100644 --- a/components/script/dom/vrdisplay.rs +++ b/components/script/dom/vrdisplay.rs @@ -161,7 +161,7 @@ impl VRDisplayMethods for VRDisplay { // https://w3c.github.io/webvr/#dom-vrdisplay-displayid fn DisplayId(&self) -> u32 { - self.display.borrow().display_id as u32 + self.display.borrow().display_id } // https://w3c.github.io/webvr/#dom-vrdisplay-displayname @@ -188,7 +188,7 @@ impl VRDisplayMethods for VRDisplay { // If not presenting we fetch inmediante VRFrameData let (sender, receiver) = ipc::channel().unwrap(); self.webvr_thread().send(WebVRMsg::GetFrameData(self.global().pipeline_id(), - self.get_display_id(), + self.DisplayId(), self.depth_near.get(), self.depth_far.get(), sender)).unwrap(); @@ -213,7 +213,7 @@ impl VRDisplayMethods for VRDisplay { fn ResetPose(&self) { let (sender, receiver) = ipc::channel().unwrap(); self.webvr_thread().send(WebVRMsg::ResetPose(self.global().pipeline_id(), - self.get_display_id(), + self.DisplayId(), sender)).unwrap(); if let Ok(data) = receiver.recv().unwrap() { // Some VRDisplay data might change after calling ResetPose() @@ -378,7 +378,7 @@ impl VRDisplayMethods for VRDisplay { } let api_sender = self.layer_ctx.get().unwrap().ipc_renderer(); - let display_id = self.display.borrow().display_id; + let display_id = self.display.borrow().display_id as u64; let layer = self.layer.borrow(); let msg = VRCompositorCommand::SubmitFrame(display_id, layer.left_bounds, layer.right_bounds); api_sender.send(CanvasMsg::WebVR(msg)).unwrap(); @@ -390,10 +390,6 @@ impl VRDisplay { self.global().as_window().webvr_thread().expect("Shouldn't arrive here with WebVR disabled") } - pub fn get_display_id(&self) -> u64 { - self.display.borrow().display_id - } - pub fn update_display(&self, display: &WebVRDisplayData) { *self.display.borrow_mut() = display.clone(); if let Some(ref stage) = display.stage_parameters { @@ -447,7 +443,7 @@ impl VRDisplay { let (sync_sender, sync_receiver) = ipc::channel().unwrap(); *self.frame_data_receiver.borrow_mut() = Some(sync_receiver); - let display_id = self.display.borrow().display_id; + let display_id = self.display.borrow().display_id as u64; let api_sender = self.layer_ctx.get().unwrap().ipc_renderer(); let js_sender = self.global().script_chan(); let address = Trusted::new(&*self); @@ -497,7 +493,7 @@ impl VRDisplay { *self.frame_data_receiver.borrow_mut() = None; let api_sender = self.layer_ctx.get().unwrap().ipc_renderer(); - let display_id = self.display.borrow().display_id; + let display_id = self.display.borrow().display_id as u64; let msg = VRCompositorCommand::Release(display_id); api_sender.send(CanvasMsg::WebVR(msg)).unwrap(); } diff --git a/components/script/dom/vrpose.rs b/components/script/dom/vrpose.rs index 542059ca799..ee035e2a0f1 100644 --- a/components/script/dom/vrpose.rs +++ b/components/script/dom/vrpose.rs @@ -32,7 +32,9 @@ unsafe fn update_or_create_typed_array(cx: *mut JSContext, match src { Some(data) => { if dst.get().is_null() { - let _ = Float32Array::create(cx, CreateWith::Slice(data), dst.handle_mut()); + rooted!(in (cx) let mut array = ptr::null_mut()); + let _ = Float32Array::create(cx, CreateWith::Slice(data), array.handle_mut()); + (*dst).set(array.get()); } else { typedarray!(in(cx) let array: Float32Array = dst.get()); if let Ok(mut array) = array { diff --git a/components/script/dom/webglrenderingcontext.rs b/components/script/dom/webglrenderingcontext.rs index f13f7f02d9b..1826a877331 100644 --- a/components/script/dom/webglrenderingcontext.rs +++ b/components/script/dom/webglrenderingcontext.rs @@ -31,6 +31,7 @@ use dom::webglframebuffer::WebGLFramebuffer; use dom::webglprogram::WebGLProgram; use dom::webglrenderbuffer::WebGLRenderbuffer; use dom::webglshader::WebGLShader; +use dom::webglshaderprecisionformat::WebGLShaderPrecisionFormat; use dom::webgltexture::{TexParameterValue, WebGLTexture}; use dom::webgluniformlocation::WebGLUniformLocation; use dom::window::Window; @@ -50,7 +51,7 @@ use webrender_traits; use webrender_traits::{WebGLCommand, WebGLError, WebGLFramebufferBindingRequest, WebGLParameter}; use webrender_traits::WebGLError::*; -type ImagePixelResult = Result<(Vec<u8>, Size2D<i32>), ()>; +type ImagePixelResult = Result<(Vec<u8>, Size2D<i32>, bool), ()>; pub const MAX_UNIFORM_AND_ATTRIBUTE_LEN: usize = 256; macro_rules! handle_potential_webgl_error { @@ -388,8 +389,17 @@ impl WebGLRenderingContext { match (format, data_type) { (TexFormat::RGBA, TexDataType::UnsignedByte) => pixels, - (TexFormat::RGB, TexDataType::UnsignedByte) => pixels, - + (TexFormat::RGB, TexDataType::UnsignedByte) => { + // Remove alpha channel + let pixel_count = pixels.len() / 4; + let mut rgb8 = Vec::<u8>::with_capacity(pixel_count * 3); + for rgba8 in pixels.chunks(4) { + rgb8.push(rgba8[0]); + rgb8.push(rgba8[1]); + rgb8.push(rgba8[2]); + } + rgb8 + }, (TexFormat::RGBA, TexDataType::UnsignedShort4444) => { let mut rgba4 = Vec::<u8>::with_capacity(pixel_count * 2); for rgba8 in pixels.chunks(4) { @@ -442,9 +452,9 @@ impl WebGLRenderingContext { // // Nontheless, since it's the error case, I'm not totally sure the // complexity is worth it. - let (pixels, size) = match source { + let (pixels, size, premultiplied) = match source { ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement::ImageData(image_data) => { - (image_data.get_data_array(), image_data.get_size()) + (image_data.get_data_array(), image_data.get_size(), false) }, ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement::HTMLImageElement(image) => { let img_url = match image.get_url() { @@ -471,7 +481,7 @@ impl WebGLRenderingContext { byte_swap(&mut data); - (data, size) + (data, size, false) }, // TODO(emilio): Getting canvas data is implemented in CanvasRenderingContext2D, // but we need to refactor it moving it to `HTMLCanvasElement` and support @@ -479,7 +489,8 @@ impl WebGLRenderingContext { ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement::HTMLCanvasElement(canvas) => { if let Some((mut data, size)) = canvas.fetch_all_data() { byte_swap(&mut data); - (data, size) + // Pixels got from Canvas have already alpha premultiplied + (data, size, true) } else { return Err(()); } @@ -488,7 +499,7 @@ impl WebGLRenderingContext { => unimplemented!(), }; - return Ok((pixels, size)); + return Ok((pixels, size, premultiplied)); } // TODO(emilio): Move this logic to a validator. @@ -643,6 +654,51 @@ impl WebGLRenderingContext { } } + // Remove premultiplied alpha. + // This is only called when texImage2D is called using a canvas2d source and + // UNPACK_PREMULTIPLY_ALPHA_WEBGL is disabled. Pixels got from a canvas2D source + // are always RGBA8 with premultiplied alpha, so we don't have to worry about + // additional formats as happens in the premultiply_pixels method. + fn remove_premultiplied_alpha(&self, mut pixels: Vec<u8>) -> Vec<u8> { + for rgba in pixels.chunks_mut(4) { + let a = (rgba[3] as f32) / 255.0; + rgba[0] = (rgba[0] as f32 / a) as u8; + rgba[1] = (rgba[1] as f32 / a) as u8; + rgba[2] = (rgba[2] as f32 / a) as u8; + } + pixels + } + + fn prepare_pixels(&self, + internal_format: TexFormat, + data_type: TexDataType, + width: u32, + height: u32, + unpacking_alignment: u32, + source_premultiplied: bool, + source_from_image_or_canvas: bool, + mut pixels: Vec<u8>) -> Vec<u8> { + let dest_premultiply = self.texture_unpacking_settings.get().contains(PREMULTIPLY_ALPHA); + if !source_premultiplied && dest_premultiply { + if source_from_image_or_canvas { + // When the pixels come from image or canvas or imagedata, use RGBA8 format + pixels = self.premultiply_pixels(TexFormat::RGBA, TexDataType::UnsignedByte, pixels); + } else { + pixels = self.premultiply_pixels(internal_format, data_type, pixels); + } + } else if source_premultiplied && !dest_premultiply { + pixels = self.remove_premultiplied_alpha(pixels); + } + + if source_from_image_or_canvas { + pixels = self.rgba8_image_to_tex_image_data(internal_format, data_type, pixels); + } + + // FINISHME: Consider doing premultiply and flip in a single mutable Vec. + self.flip_teximage_y(pixels, internal_format, data_type, + width as usize, height as usize, unpacking_alignment as usize) + } + fn tex_image_2d(&self, texture: Root<WebGLTexture>, target: TexImageTarget, @@ -654,11 +710,6 @@ impl WebGLRenderingContext { _border: u32, unpacking_alignment: u32, pixels: Vec<u8>) { // NB: pixels should NOT be premultipied - // FINISHME: Consider doing premultiply and flip in a single mutable Vec. - let pixels = self.premultiply_pixels(internal_format, data_type, pixels); - - let pixels = self.flip_teximage_y(pixels, internal_format, data_type, - width as usize, height as usize, unpacking_alignment as usize); // TexImage2D depth is always equal to 1 handle_potential_webgl_error!(self, texture.initialize(target, @@ -704,7 +755,7 @@ impl WebGLRenderingContext { format: TexFormat, data_type: TexDataType, unpacking_alignment: u32, - pixels: Vec<u8>) { // NB: pixels should NOT be premultipied + pixels: Vec<u8>) { // We have already validated level let image_info = texture.image_info_for_target(&target, level); @@ -723,12 +774,6 @@ impl WebGLRenderingContext { return self.webgl_error(InvalidOperation); } - // FINISHME: Consider doing premultiply and flip in a single mutable Vec. - let pixels = self.premultiply_pixels(format, data_type, pixels); - - let pixels = self.flip_teximage_y(pixels, format, data_type, - width as usize, height as usize, unpacking_alignment as usize); - // Set the unpack alignment. For textures coming from arrays, // this will be the current value of the context's // GL_UNPACK_ALIGNMENT, while for textures from images or @@ -1795,7 +1840,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { // WebGl conformance expects error with null programs. Check tests in get-active-test.html self.webgl_error(InvalidValue); return None; - } + } }; match program.get_active_uniform(index) { @@ -1822,7 +1867,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { // WebGl conformance expects error with null programs. Check tests in get-active-test.html self.webgl_error(InvalidValue); return None; - } + } }; match program.get_active_attrib(index) { @@ -1902,6 +1947,27 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { } } + // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9 + fn GetShaderPrecisionFormat(&self, + shader_type: u32, + precision_type: u32) + -> Option<Root<WebGLShaderPrecisionFormat>> { + let (sender, receiver) = webrender_traits::channel::msg_channel().unwrap(); + self.ipc_renderer.send(CanvasMsg::WebGL(WebGLCommand::GetShaderPrecisionFormat(shader_type, + precision_type, + sender))) + .unwrap(); + match receiver.recv().unwrap() { + Ok((range_min, range_max, precision)) => { + Some(WebGLShaderPrecisionFormat::new(self.global().as_window(), range_min, range_max, precision)) + }, + Err(error) => { + self.webgl_error(error); + None + } + } + } + // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 fn GetUniformLocation(&self, program: Option<&WebGLProgram>, @@ -2819,8 +2885,11 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { return Ok(self.webgl_error(InvalidOperation)); } + let pixels = self.prepare_pixels(format, data_type, width, height, + unpacking_alignment, false, false, buff); + self.tex_image_2d(texture, target, data_type, format, - level, width, height, border, unpacking_alignment, buff); + level, width, height, border, unpacking_alignment, pixels); Ok(()) } @@ -2834,8 +2903,8 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { data_type: u32, source: Option<ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement>) -> Fallible<()> { // Get pixels from image source - let (pixels, size) = match self.get_image_pixels(source) { - Ok((pixels, size)) => (pixels, size), + let (pixels, size, premultiplied) = match self.get_image_pixels(source) { + Ok((pixels, size, premultiplied)) => (pixels, size, premultiplied), Err(_) => return Ok(()), }; @@ -2858,7 +2927,9 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { Err(_) => return Ok(()), // NB: The validator sets the correct error for us. }; - let pixels = self.rgba8_image_to_tex_image_data(format, data_type, pixels); + let unpacking_alignment = 1; + let pixels = self.prepare_pixels(format, data_type, width, height, + unpacking_alignment, premultiplied, true, pixels); self.tex_image_2d(texture, target, data_type, format, level, width, height, border, 1, pixels); @@ -2929,8 +3000,12 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { return Ok(self.webgl_error(InvalidOperation)); } + let unpacking_alignment = self.texture_unpacking_alignment.get(); + let pixels = self.prepare_pixels(format, data_type, width, height, + unpacking_alignment, false, false, buff); + self.tex_sub_image_2d(texture, target, level, xoffset, yoffset, - width, height, format, data_type, unpacking_alignment, buff); + width, height, format, data_type, unpacking_alignment, pixels); Ok(()) } @@ -2944,8 +3019,8 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { data_type: u32, source: Option<ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement>) -> Fallible<()> { - let (pixels, size) = match self.get_image_pixels(source) { - Ok((pixels, size)) => (pixels, size), + let (pixels, size, premultiplied) = match self.get_image_pixels(source) { + Ok((pixels, size, premultiplied)) => (pixels, size, premultiplied), Err(_) => return Ok(()), }; @@ -2966,7 +3041,9 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { Err(_) => return Ok(()), // NB: The validator sets the correct error for us. }; - let pixels = self.rgba8_image_to_tex_image_data(format, data_type, pixels); + let unpacking_alignment = 1; + let pixels = self.prepare_pixels(format, data_type, width, height, + unpacking_alignment, premultiplied, true, pixels); self.tex_sub_image_2d(texture, target, level, xoffset, yoffset, width, height, format, data_type, 1, pixels); diff --git a/components/script/dom/webglshader.rs b/components/script/dom/webglshader.rs index 1526b5303f7..b66151bf353 100644 --- a/components/script/dom/webglshader.rs +++ b/components/script/dom/webglshader.rs @@ -106,9 +106,11 @@ impl WebGLShader { } if let Some(ref source) = *self.source.borrow() { + let mut params = BuiltInResources::default(); + params.FragmentPrecisionHigh = 1; let validator = ShaderValidator::for_webgl(self.gl_type, SHADER_OUTPUT_FORMAT, - &BuiltInResources::default()).unwrap(); + ¶ms).unwrap(); match validator.compile_and_translate(&[source]) { Ok(translated_source) => { debug!("Shader translated: {}", translated_source); diff --git a/components/script/dom/webidls/CSSStyleDeclaration.webidl b/components/script/dom/webidls/CSSStyleDeclaration.webidl index 498d4a634d4..efe1d57b038 100644 --- a/components/script/dom/webidls/CSSStyleDeclaration.webidl +++ b/components/script/dom/webidls/CSSStyleDeclaration.webidl @@ -33,6 +33,8 @@ interface CSSStyleDeclaration { }; partial interface CSSStyleDeclaration { + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString all; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString background; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString backgroundColor; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString background-color; @@ -281,6 +283,8 @@ partial interface CSSStyleDeclaration { [SetterThrows, TreatNullAs=EmptyString] attribute DOMString font-style; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString fontVariant; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString font-variant; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString fontVariantCaps; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString font-variant-caps; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString fontWeight; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString font-weight; diff --git a/components/script/dom/webidls/Element.webidl b/components/script/dom/webidls/Element.webidl index 1662dda36d6..c099b2a04ae 100644 --- a/components/script/dom/webidls/Element.webidl +++ b/components/script/dom/webidls/Element.webidl @@ -81,7 +81,8 @@ interface Element : Node { // http://dev.w3.org/csswg/cssom-view/#extensions-to-the-element-interface partial interface Element { - DOMRectList getClientRects(); + sequence<DOMRect> getClientRects(); + [NewObject] DOMRect getBoundingClientRect(); void scroll(optional ScrollToOptions options); diff --git a/components/script/dom/webidls/Gamepad.webidl b/components/script/dom/webidls/Gamepad.webidl new file mode 100644 index 00000000000..0f666a495cf --- /dev/null +++ b/components/script/dom/webidls/Gamepad.webidl @@ -0,0 +1,26 @@ +/* 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/. */ + +// https://w3c.github.io/gamepad/#gamepad-interface +[Pref="dom.gamepad.enabled"] +interface Gamepad { + readonly attribute DOMString id; + readonly attribute long index; + readonly attribute boolean connected; + readonly attribute DOMHighResTimeStamp timestamp; + readonly attribute DOMString mapping; + readonly attribute Float64Array axes; + [SameObject] readonly attribute GamepadButtonList buttons; +}; + +// https://w3c.github.io/gamepad/extensions.html#dom-gamepad +partial interface Gamepad { + readonly attribute DOMString hand; + readonly attribute VRPose? pose; +}; + +// https://w3c.github.io/webvr/spec/1.1/#interface-gamepad +partial interface Gamepad { + readonly attribute unsigned long displayId; +}; diff --git a/components/script/dom/webidls/GamepadButton.webidl b/components/script/dom/webidls/GamepadButton.webidl new file mode 100644 index 00000000000..2fa04c8ba3c --- /dev/null +++ b/components/script/dom/webidls/GamepadButton.webidl @@ -0,0 +1,11 @@ +/* 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/. */ + +// https://w3c.github.io/gamepad/#gamepadbutton-interface +[Pref="dom.gamepad.enabled"] +interface GamepadButton { + readonly attribute boolean pressed; + readonly attribute boolean touched; + readonly attribute double value; +}; diff --git a/components/script/dom/webidls/DOMRectList.webidl b/components/script/dom/webidls/GamepadButtonList.webidl index dfb33639c93..c8fb75a4350 100644 --- a/components/script/dom/webidls/DOMRectList.webidl +++ b/components/script/dom/webidls/GamepadButtonList.webidl @@ -2,10 +2,9 @@ * 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/. */ -// http://dev.w3.org/fxtf/geometry/#DOMRectList -[NoInterfaceObject, Exposed=(Window,Worker)] -//[ArrayClass] -interface DOMRectList { +// https://w3c.github.io/gamepad/#dom-gamepad-buttons +[Pref="dom.gamepad.enabled"] +interface GamepadButtonList { + getter GamepadButton? item(unsigned long index); readonly attribute unsigned long length; - getter DOMRect? item(unsigned long index); }; diff --git a/components/script/dom/webidls/GamepadEvent.webidl b/components/script/dom/webidls/GamepadEvent.webidl new file mode 100644 index 00000000000..ea40fd4261c --- /dev/null +++ b/components/script/dom/webidls/GamepadEvent.webidl @@ -0,0 +1,13 @@ +/* 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/. */ + +// https://w3c.github.io/gamepad/#gamepadevent-interface +[Pref="dom.gamepad.enabled", Constructor(DOMString type, GamepadEventInit eventInitDict)] +interface GamepadEvent : Event { + readonly attribute Gamepad gamepad; +}; + +dictionary GamepadEventInit : EventInit { + required Gamepad gamepad; +}; diff --git a/components/script/dom/webidls/GamepadList.webidl b/components/script/dom/webidls/GamepadList.webidl new file mode 100644 index 00000000000..2d99e4e8f3c --- /dev/null +++ b/components/script/dom/webidls/GamepadList.webidl @@ -0,0 +1,10 @@ +/* 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/. */ + +// https://w3c.github.io/gamepad/#navigator-interface-extension +[Pref="dom.gamepad.enabled"] +interface GamepadList { + getter Gamepad? item(unsigned long index); + readonly attribute unsigned long length; +}; diff --git a/components/script/dom/webidls/Navigator.webidl b/components/script/dom/webidls/Navigator.webidl index e04616ef40f..338753d6261 100644 --- a/components/script/dom/webidls/Navigator.webidl +++ b/components/script/dom/webidls/Navigator.webidl @@ -68,3 +68,8 @@ partial interface Navigator { partial interface Navigator { [Pref="dom.permissions.enabled"] readonly attribute Permissions permissions; }; + +// https://w3c.github.io/gamepad/#navigator-interface-extension +partial interface Navigator { + [Pref="dom.gamepad.enabled"] GamepadList getGamepads(); +}; diff --git a/components/script/dom/webidls/Range.webidl b/components/script/dom/webidls/Range.webidl index c40f2ecb161..1daf2d8182f 100644 --- a/components/script/dom/webidls/Range.webidl +++ b/components/script/dom/webidls/Range.webidl @@ -82,6 +82,7 @@ partial interface Range { // http://dev.w3.org/csswg/cssom-view/#extensions-to-the-range-interface partial interface Range { - // DOMRectList? getClientRects(); + // sequence<DOMRect> getClientRects(); + // [NewObject] // DOMRect getBoundingClientRect(); }; diff --git a/components/script/dom/webidls/WebGLRenderingContext.webidl b/components/script/dom/webidls/WebGLRenderingContext.webidl index ef0be0f9465..0d4c14a05df 100644 --- a/components/script/dom/webidls/WebGLRenderingContext.webidl +++ b/components/script/dom/webidls/WebGLRenderingContext.webidl @@ -595,7 +595,7 @@ interface WebGLRenderingContextBase DOMString? getProgramInfoLog(WebGLProgram? program); //any getRenderbufferParameter(GLenum target, GLenum pname); any getShaderParameter(WebGLShader? shader, GLenum pname); - //WebGLShaderPrecisionFormat? getShaderPrecisionFormat(GLenum shadertype, GLenum precisiontype); + WebGLShaderPrecisionFormat? getShaderPrecisionFormat(GLenum shadertype, GLenum precisiontype); DOMString? getShaderInfoLog(WebGLShader? shader); DOMString? getShaderSource(WebGLShader? shader); diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index 6a264c86d6b..6b8857b81b1 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -52,7 +52,6 @@ use dom::testrunner::TestRunner; use dom_struct::dom_struct; use euclid::{Point2D, Rect, Size2D}; use fetch; -use gfx_traits::ScrollRootId; use ipc_channel::ipc::{self, IpcSender}; use ipc_channel::router::ROUTER; use js::jsapi::{HandleObject, HandleValue, JSAutoCompartment, JSContext}; @@ -86,13 +85,15 @@ use servo_atoms::Atom; use servo_config::opts; use servo_config::prefs::PREFS; use servo_geometry::{f32_rect_to_au_rect, max_rect}; -use servo_url::{ImmutableOrigin, ServoUrl}; +use servo_url::{Host, MutableOrigin, ImmutableOrigin, ServoUrl}; use std::ascii::AsciiExt; use std::borrow::ToOwned; use std::cell::Cell; use std::collections::{HashMap, HashSet}; use std::collections::hash_map::Entry; use std::default::Default; +use std::env; +use std::fs; use std::io::{Write, stderr, stdout}; use std::mem; use std::rc::Rc; @@ -103,10 +104,12 @@ use std::sync::mpsc::TryRecvError::{Disconnected, Empty}; use style::context::ReflowGoal; use style::error_reporting::ParseErrorReporter; use style::media_queries; +use style::parser::{LengthParsingMode, ParserContext as CssParserContext}; use style::properties::PropertyId; use style::properties::longhands::overflow_x; use style::selector_parser::PseudoElement; use style::str::HTML_SPACE_CHARACTERS; +use style::stylesheets::CssRuleType; use task_source::dom_manipulation::DOMManipulationTaskSource; use task_source::file_reading::FileReadingTaskSource; use task_source::history_traversal::HistoryTraversalTaskSource; @@ -118,6 +121,7 @@ use timers::{IsInterval, TimerCallback}; use tinyfiledialogs::{self, MessageBoxIcon}; use url::Position; use webdriver_handlers::jsval_to_webdriver; +use webrender_traits::ClipId; use webvr_traits::WebVRMsg; /// Current state of the window object @@ -265,6 +269,10 @@ pub struct Window { /// to ensure that the element can be marked dirty when the image data becomes /// available at some point in the future. pending_layout_images: DOMRefCell<HashMap<PendingImageId, Vec<JS<Node>>>>, + + /// Directory to store unminified scripts for this window if unminify-js + /// opt is enabled. + unminified_js_dir: DOMRefCell<Option<String>>, } impl Window { @@ -278,6 +286,10 @@ impl Window { } } + pub fn origin(&self) -> &MutableOrigin { + self.globalscope.origin() + } + pub fn get_cx(&self) -> *mut JSContext { self.js_runtime.borrow().as_ref().unwrap().cx() } @@ -963,7 +975,12 @@ impl WindowMethods for Window { // https://drafts.csswg.org/cssom-view/#dom-window-matchmedia fn MatchMedia(&self, query: DOMString) -> Root<MediaQueryList> { let mut parser = Parser::new(&query); - let media_query_list = media_queries::parse_media_query_list(&mut parser); + let url = self.get_url(); + let quirks_mode = self.Document().quirks_mode(); + let context = CssParserContext::new_for_cssom(&url, self.css_error_reporter(), Some(CssRuleType::Media), + LengthParsingMode::Default, + quirks_mode); + let media_query_list = media_queries::parse_media_query_list(&context, &mut parser); let document = self.Document(); let mql = MediaQueryList::new(&document, media_query_list); self.media_query_lists.push(&*mql); @@ -1066,9 +1083,10 @@ impl Window { //TODO Step 11 //let document = self.Document(); // Step 12 + let global_scope = self.upcast::<GlobalScope>(); self.perform_a_scroll(x.to_f32().unwrap_or(0.0f32), y.to_f32().unwrap_or(0.0f32), - ScrollRootId::root(), + global_scope.pipeline_id().root_scroll_node(), behavior, None); } @@ -1077,7 +1095,7 @@ impl Window { pub fn perform_a_scroll(&self, x: f32, y: f32, - scroll_root_id: ScrollRootId, + scroll_root_id: ClipId, behavior: ScrollBehavior, element: Option<&Element>) { //TODO Step 1 @@ -1097,8 +1115,7 @@ impl Window { self.update_viewport_for_scroll(x, y); let global_scope = self.upcast::<GlobalScope>(); - let message = ConstellationMsg::ScrollFragmentPoint( - global_scope.pipeline_id(), scroll_root_id, point, smooth); + let message = ConstellationMsg::ScrollFragmentPoint(scroll_root_id, point, smooth); global_scope.constellation_chan().send(message).unwrap(); } @@ -1484,6 +1501,23 @@ impl Window { assert!(self.document.get().is_none()); assert!(document.window() == self); self.document.set(Some(&document)); + if !opts::get().unminify_js { + return; + } + // Create a folder for the document host to store unminified scripts. + if let Some(&Host::Domain(ref host)) = document.url().origin().host() { + let mut path = env::current_dir().unwrap(); + path.push("unminified-js"); + path.push(host); + let _ = fs::remove_dir_all(&path); + match fs::create_dir_all(&path) { + Ok(_) => { + *self.unminified_js_dir.borrow_mut() = Some(path.into_os_string().into_string().unwrap()); + debug!("Created folder for {:?} unminified scripts {:?}", host, self.unminified_js_dir.borrow()); + }, + Err(_) => warn!("Could not create unminified dir for {:?}", host), + } + } } /// Commence a new URL load which will either replace this window or scroll to a fragment. @@ -1691,6 +1725,10 @@ impl Window { self.upcast::<GlobalScope>().slow_down_timers(); } } + + pub fn unminified_js_dir(&self) -> Option<String> { + self.unminified_js_dir.borrow().clone() + } } impl Window { @@ -1717,6 +1755,7 @@ impl Window { id: PipelineId, parent_info: Option<(PipelineId, FrameType)>, window_size: Option<WindowSizeData>, + origin: MutableOrigin, webvr_thread: Option<IpcSender<WebVRMsg>>) -> Root<Window> { let layout_rpc: Box<LayoutRPC + Send> = { @@ -1739,7 +1778,8 @@ impl Window { constellation_chan, scheduler_chan, resource_threads, - timer_event_chan), + timer_event_chan, + origin), script_chan: script_chan, dom_manipulation_task_source: dom_task_source, user_interaction_task_source: user_task_source, @@ -1785,6 +1825,7 @@ impl Window { webvr_thread: webvr_thread, permission_state_invocation_results: DOMRefCell::new(HashMap::new()), pending_layout_images: DOMRefCell::new(HashMap::new()), + unminified_js_dir: DOMRefCell::new(None), }; unsafe { diff --git a/components/script/dom/workerglobalscope.rs b/components/script/dom/workerglobalscope.rs index 948d72c251b..fd1f03db1e3 100644 --- a/components/script/dom/workerglobalscope.rs +++ b/components/script/dom/workerglobalscope.rs @@ -37,7 +37,7 @@ use script_runtime::{CommonScriptMsg, ScriptChan, ScriptPort}; use script_thread::RunnableWrapper; use script_traits::{TimerEvent, TimerEventId}; use script_traits::WorkerGlobalScopeInit; -use servo_url::ServoUrl; +use servo_url::{MutableOrigin, ServoUrl}; use std::default::Default; use std::rc::Rc; use std::sync::Arc; @@ -59,6 +59,7 @@ pub fn prepare_workerscope_init(global: &GlobalScope, scheduler_chan: global.scheduler_chan().clone(), worker_id: global.get_next_worker_id(), pipeline_id: global.pipeline_id(), + origin: global.origin().immutable().clone(), }; init @@ -109,7 +110,8 @@ impl WorkerGlobalScope { init.constellation_chan, init.scheduler_chan, init.resource_threads, - timer_event_chan), + timer_event_chan, + MutableOrigin::new(init.origin)), worker_id: init.worker_id, worker_url: worker_url, closing: closing, diff --git a/components/script/layout_wrapper.rs b/components/script/layout_wrapper.rs index 4b118bdf7f1..3ddb1d2b5c9 100644 --- a/components/script/layout_wrapper.rs +++ b/components/script/layout_wrapper.rs @@ -55,6 +55,7 @@ use servo_atoms::Atom; use servo_url::ServoUrl; use std::fmt; use std::fmt::Debug; +use std::hash::{Hash, Hasher}; use std::marker::PhantomData; use std::mem::transmute; use std::sync::Arc; @@ -460,12 +461,16 @@ impl<'le> TElement for ServoLayoutElement<'le> { self.element.has_selector_flags(flags) } - fn has_animations(&self, _pseudo: Option<&PseudoElement>) -> bool { - panic!("this should be only called on gecko"); + fn has_animations(&self) -> bool { + unreachable!("this should be only called on gecko"); } - fn has_css_animations(&self, _pseudo: Option<&PseudoElement>) -> bool { - panic!("this should be only called on gecko"); + fn has_css_animations(&self) -> bool { + unreachable!("this should be only called on gecko"); + } + + fn has_css_transitions(&self) -> bool { + unreachable!("this should be only called on gecko"); } } @@ -475,6 +480,14 @@ impl<'le> PartialEq for ServoLayoutElement<'le> { } } +impl<'le> Hash for ServoLayoutElement<'le> { + fn hash<H: Hasher>(&self, state: &mut H) { + self.element.hash(state); + } +} + +impl<'le> Eq for ServoLayoutElement<'le> {} + impl<'le> ServoLayoutElement<'le> { fn from_layout_js(el: LayoutJS<Element>) -> ServoLayoutElement<'le> { ServoLayoutElement { diff --git a/components/script/lib.rs b/components/script/lib.rs index 2de2e37ea38..678603ee60f 100644 --- a/components/script/lib.rs +++ b/components/script/lib.rs @@ -12,7 +12,6 @@ #![feature(optin_builtin_traits)] #![feature(plugin)] #![feature(proc_macro)] -#![feature(slice_patterns)] #![feature(stmt_expr_attributes)] #![feature(try_from)] #![feature(untagged_unions)] @@ -97,6 +96,7 @@ extern crate style_traits; extern crate time; #[cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))] extern crate tinyfiledialogs; +extern crate unicode_segmentation; extern crate url; extern crate uuid; extern crate webrender_traits; diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index 955887addf4..96167cec236 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -27,6 +27,7 @@ use dom::bindings::cell::DOMRefCell; use dom::bindings::codegen::Bindings::CSSStyleDeclarationBinding::CSSStyleDeclarationMethods; use dom::bindings::codegen::Bindings::DocumentBinding::{DocumentMethods, DocumentReadyState}; use dom::bindings::codegen::Bindings::EventBinding::EventInit; +use dom::bindings::codegen::Bindings::NavigatorBinding::NavigatorMethods; use dom::bindings::codegen::Bindings::TransitionEventBinding::TransitionEventInit; use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; use dom::bindings::conversions::{ConversionResult, FromJSValConvertible, StringificationBehavior}; @@ -91,7 +92,6 @@ use script_traits::{ScriptThreadFactory, TimerEvent, TimerSchedulerMsg, TimerSou use script_traits::{TouchEventType, TouchId, UntrustedNodeAddress, WindowSizeData, WindowSizeType}; use script_traits::CompositorEvent::{KeyEvent, MouseButtonEvent, MouseMoveEvent, ResizeEvent}; use script_traits::CompositorEvent::{TouchEvent, TouchpadPressureEvent}; -use script_traits::WebVREventMsg; use script_traits::webdriver_msg::WebDriverScriptCommand; use serviceworkerjob::{Job, JobQueue, AsyncJobHandler}; use servo_config::opts; @@ -119,7 +119,7 @@ use task_source::user_interaction::{UserInteractionTask, UserInteractionTaskSour use time::Tm; use url::Position; use webdriver_handlers; -use webvr_traits::WebVRMsg; +use webvr_traits::{WebVREvent, WebVRMsg}; pub type ImageCacheMsg = (PipelineId, PendingImageResponse); @@ -1070,8 +1070,8 @@ impl ScriptThread { self.handle_reload(pipeline_id), ConstellationControlMsg::ExitPipeline(pipeline_id, discard_browsing_context) => self.handle_exit_pipeline_msg(pipeline_id, discard_browsing_context), - ConstellationControlMsg::WebVREvent(pipeline_id, event) => - self.handle_webvr_event(pipeline_id, event), + ConstellationControlMsg::WebVREvents(pipeline_id, events) => + self.handle_webvr_events(pipeline_id, events), msg @ ConstellationControlMsg::AttachLayout(..) | msg @ ConstellationControlMsg::Viewport(..) | msg @ ConstellationControlMsg::SetScrollState(..) | @@ -1784,6 +1784,7 @@ impl ScriptThread { incomplete.pipeline_id, incomplete.parent_info, incomplete.window_size, + incomplete.origin.clone(), self.webvr_thread.clone()); // Initialize the browsing context for the window. @@ -2186,11 +2187,11 @@ impl ScriptThread { } } - fn handle_webvr_event(&self, pipeline_id: PipelineId, event: WebVREventMsg) { + fn handle_webvr_events(&self, pipeline_id: PipelineId, events: Vec<WebVREvent>) { let window = self.documents.borrow().find_window(pipeline_id); if let Some(window) = window { - let navigator = window.Navigator(); - navigator.handle_webvr_event(event); + let vr = window.Navigator().Vr(); + vr.handle_webvr_events(events); } } diff --git a/components/script/stylesheet_loader.rs b/components/script/stylesheet_loader.rs index cdafde510c9..14b8c385e39 100644 --- a/components/script/stylesheet_loader.rs +++ b/components/script/stylesheet_loader.rs @@ -22,7 +22,6 @@ use ipc_channel::router::ROUTER; use net_traits::{FetchResponseListener, FetchMetadata, FilteredMetadata, Metadata, NetworkError, ReferrerPolicy}; use net_traits::request::{CorsSettings, CredentialsMode, Destination, RequestInit, RequestMode, Type as RequestType}; use network_listener::{NetworkListener, PreInvoke}; -use script_layout_interface::message::Msg; use servo_url::ServoUrl; use std::mem; use std::sync::{Arc, Mutex}; @@ -145,15 +144,14 @@ impl FetchResponseListener for StylesheetContext { media.take().unwrap(), shared_lock, Some(&loader), - win.css_error_reporter())); + win.css_error_reporter(), + document.quirks_mode())); if link.is_alternate() { sheet.set_disabled(true); } - link.set_stylesheet(sheet.clone()); - - win.layout_chan().send(Msg::AddStylesheet(sheet)).unwrap(); + link.set_stylesheet(sheet); } } StylesheetContextSource::Import(ref stylesheet) => { @@ -271,15 +269,15 @@ impl<'a> StylesheetLoader<'a> { impl<'a> StyleStylesheetLoader for StylesheetLoader<'a> { fn request_stylesheet( &self, - media: MediaList, - make_import: &mut FnMut(MediaList) -> ImportRule, + media: Arc<StyleLocked<MediaList>>, + make_import: &mut FnMut(Arc<StyleLocked<MediaList>>) -> ImportRule, make_arc: &mut FnMut(ImportRule) -> Arc<StyleLocked<ImportRule>>, ) -> Arc<StyleLocked<ImportRule>> { let import = make_import(media); let url = import.url.url().expect("Invalid urls shouldn't enter the loader").clone(); - //TODO (mrnayak) : Whether we should use the original loader's CORS setting? - //Fix this when spec has more details. + // TODO (mrnayak) : Whether we should use the original loader's CORS + // setting? Fix this when spec has more details. let source = StylesheetContextSource::Import(import.stylesheet.clone()); self.load(source, url, None, "".to_owned()); diff --git a/components/script/textinput.rs b/components/script/textinput.rs index 5f04b5b187e..144be9dbd78 100644 --- a/components/script/textinput.rs +++ b/components/script/textinput.rs @@ -14,6 +14,7 @@ use std::cmp::{max, min}; use std::default::Default; use std::ops::Range; use std::usize; +use unicode_segmentation::UnicodeSegmentation; #[derive(Copy, Clone, PartialEq)] pub enum Selection { @@ -376,18 +377,16 @@ impl<T: ClipboardProvider> TextInput<T> { } let adjust = { let current_line = &self.lines[self.edit_point.line]; - // FIXME: We adjust by one code point, but it proably should be one grapheme cluster - // https://github.com/unicode-rs/unicode-segmentation match direction { Direction::Forward => { - match current_line[self.edit_point.index..].chars().next() { - Some(c) => c.len_utf8() as isize, + match current_line[self.edit_point.index..].graphemes(true).next() { + Some(c) => c.len() as isize, None => 1, // Going to the next line is a "one byte" offset } } Direction::Backward => { - match current_line[..self.edit_point.index].chars().next_back() { - Some(c) => -(c.len_utf8() as isize), + match current_line[..self.edit_point.index].graphemes(true).next_back() { + Some(c) => -(c.len() as isize), None => -1, // Going to the previous line is a "one byte" offset } } @@ -468,6 +467,111 @@ impl<T: ClipboardProvider> TextInput<T> { self.selection_begin = None; } + pub fn adjust_horizontal_by_word(&mut self, direction: Direction, select: Selection) { + if self.adjust_selection_for_horizontal_change(direction, select) { + return + } + let shift_increment: isize = { + let input: &str; + match direction { + Direction::Backward => { + let remaining = self.edit_point.index; + let current_line = self.edit_point.line; + let mut newline_adjustment = 0; + if remaining == 0 && current_line > 0 { + input = &self + .lines[current_line-1]; + newline_adjustment = 1; + } else { + input = &self + .lines[current_line] + [..remaining]; + } + + let mut iter = input.split_word_bounds().rev(); + let mut shift_temp: isize = 0; + loop { + match iter.next() { + None => break, + Some(x) => { + shift_temp += - (x.len() as isize); + if x.chars().any(|x| x.is_alphabetic() || x.is_numeric()) { + break; + } + } + } + } + shift_temp - newline_adjustment + } + Direction::Forward => { + let remaining = self.current_line_length() - self.edit_point.index; + let current_line = self.edit_point.line; + let mut newline_adjustment = 0; + if remaining == 0 && self.lines.len() > self.edit_point.line + 1 { + input = &self + .lines[current_line + 1]; + newline_adjustment = 1; + } else { + input = &self + .lines[current_line] + [self.edit_point.index..]; + } + + let mut iter = input.split_word_bounds(); + let mut shift_temp: isize = 0; + loop { + match iter.next() { + None => break, + Some(x) => { + shift_temp += x.len() as isize; + if x.chars().any(|x| x.is_alphabetic() || x.is_numeric()) { + break; + } + } + } + } + shift_temp + newline_adjustment + } + } + }; + + self.adjust_horizontal(shift_increment, select); + } + + pub fn adjust_horizontal_to_line_end(&mut self, direction: Direction, select: Selection) { + if self.adjust_selection_for_horizontal_change(direction, select) { + return + } + let shift: isize = { + let current_line = &self.lines[self.edit_point.line]; + match direction { + Direction::Backward => { + - (current_line[..self.edit_point.index].len() as isize) + }, + Direction::Forward => { + current_line[self.edit_point.index..].len() as isize + } + } + }; + self.perform_horizontal_adjustment(shift, select); + } + + pub fn adjust_horizontal_to_limit(&mut self, direction: Direction, select: Selection) { + if self.adjust_selection_for_horizontal_change(direction, select) { + return + } + match direction { + Direction::Backward => { + self.edit_point.line = 0; + self.edit_point.index = 0; + }, + Direction::Forward => { + self.edit_point.line = &self.lines.len() - 1; + self.edit_point.index = (&self.lines[&self.lines.len() - 1]).len(); + } + } + } + /// Process a given `KeyboardEvent` and return an action for the caller to execute. pub fn handle_keydown(&mut self, event: &KeyboardEvent) -> KeyReaction { if let Some(key) = event.get_key() { @@ -483,6 +587,32 @@ impl<T: ClipboardProvider> TextInput<T> { mods: KeyModifiers) -> KeyReaction { let maybe_select = if mods.contains(SHIFT) { Selection::Selected } else { Selection::NotSelected }; match (printable, key) { + (_, Key::B) if mods.contains(CONTROL | ALT) => { + self.adjust_horizontal_by_word(Direction::Backward, maybe_select); + KeyReaction::RedrawSelection + }, + (_, Key::F) if mods.contains(CONTROL | ALT) => { + self.adjust_horizontal_by_word(Direction::Forward, maybe_select); + KeyReaction::RedrawSelection + }, + (_, Key::A) if mods.contains(CONTROL | ALT) => { + self.adjust_horizontal_to_line_end(Direction::Backward, maybe_select); + KeyReaction::RedrawSelection + }, + (_, Key::E) if mods.contains(CONTROL | ALT) => { + self.adjust_horizontal_to_line_end(Direction::Forward, maybe_select); + KeyReaction::RedrawSelection + }, + #[cfg(target_os = "macos")] + (None, Key::A) if mods == CONTROL => { + self.adjust_horizontal_to_line_end(Direction::Backward, maybe_select); + KeyReaction::RedrawSelection + }, + #[cfg(target_os = "macos")] + (None, Key::E) if mods == CONTROL => { + self.adjust_horizontal_to_line_end(Direction::Forward, maybe_select); + KeyReaction::RedrawSelection + }, (Some('a'), _) if is_control_key(mods) => { self.select_all(); KeyReaction::RedrawSelection @@ -501,49 +631,85 @@ impl<T: ClipboardProvider> TextInput<T> { (Some(c), _) => { self.insert_char(c); KeyReaction::DispatchInput - } + }, + #[cfg(target_os = "macos")] + (None, Key::Home) => { + KeyReaction::RedrawSelection + }, + #[cfg(target_os = "macos")] + (None, Key::End) => { + KeyReaction::RedrawSelection + }, (None, Key::Delete) => { self.delete_char(Direction::Forward); KeyReaction::DispatchInput - } + }, (None, Key::Backspace) => { self.delete_char(Direction::Backward); KeyReaction::DispatchInput - } + }, + #[cfg(target_os = "macos")] + (None, Key::Left) if mods.contains(SUPER) => { + self.adjust_horizontal_to_line_end(Direction::Backward, maybe_select); + KeyReaction::RedrawSelection + }, + #[cfg(target_os = "macos")] + (None, Key::Right) if mods.contains(SUPER) => { + self.adjust_horizontal_to_line_end(Direction::Forward, maybe_select); + KeyReaction::RedrawSelection + }, + #[cfg(target_os = "macos")] + (None, Key::Up) if mods.contains(SUPER) => { + self.adjust_horizontal_to_limit(Direction::Backward, maybe_select); + KeyReaction::RedrawSelection + }, + #[cfg(target_os = "macos")] + (None, Key::Down) if mods.contains(SUPER) => { + self.adjust_horizontal_to_limit(Direction::Forward, maybe_select); + KeyReaction::RedrawSelection + }, + (None, Key::Left) if mods.contains(ALT) => { + self.adjust_horizontal_by_word(Direction::Backward, maybe_select); + KeyReaction::RedrawSelection + }, + (None, Key::Right) if mods.contains(ALT) => { + self.adjust_horizontal_by_word(Direction::Forward, maybe_select); + KeyReaction::RedrawSelection + }, (None, Key::Left) => { self.adjust_horizontal_by_one(Direction::Backward, maybe_select); KeyReaction::RedrawSelection - } + }, (None, Key::Right) => { self.adjust_horizontal_by_one(Direction::Forward, maybe_select); KeyReaction::RedrawSelection - } + }, (None, Key::Up) => { self.adjust_vertical(-1, maybe_select); KeyReaction::RedrawSelection - } + }, (None, Key::Down) => { self.adjust_vertical(1, maybe_select); KeyReaction::RedrawSelection - } + }, (None, Key::Enter) | (None, Key::KpEnter) => self.handle_return(), (None, Key::Home) => { self.edit_point.index = 0; KeyReaction::RedrawSelection - } + }, (None, Key::End) => { self.edit_point.index = self.current_line_length(); self.assert_ok_selection(); KeyReaction::RedrawSelection - } + }, (None, Key::PageUp) => { self.adjust_vertical(-28, maybe_select); KeyReaction::RedrawSelection - } + }, (None, Key::PageDown) => { self.adjust_vertical(28, maybe_select); KeyReaction::RedrawSelection - } + }, _ => KeyReaction::Nothing, } } @@ -677,4 +843,12 @@ impl<T: ClipboardProvider> TextInput<T> { selection_start as u32 } + + pub fn set_edit_point_index(&mut self, index: usize) { + let byte_size = self.lines[self.edit_point.line] + .graphemes(true) + .take(index) + .fold(0, |acc, x| acc + x.len()); + self.edit_point.index = byte_size; + } } |