mirror of
https://github.com/servo/servo.git
synced 2025-08-06 14:10:11 +01:00
Auto merge of #23154 - robert-snakard:issue_22843, r=paulrouget
Implement WheelEvent interface Created a new dom interface: "WheelEvent" and added WPT tests to confirm the interface works. To do this I had to do the following: - Create a new `WheelEvent` dom interface. It can be found in `script/dom/wheelevent.rs` and `dom/webidls/WheelEvent.webidl` - Add a new `WheelEvent` option to the compositor's `CompositorEvent` enum - Add a new `Wheel` option to the compositor's `WindowEvent` enum - Add a new `WheelDelta` type to the `script_traits` module - Modify the `scroll_event` logic. Now we send a `WheelEvent` before scrolling. Repeat: we send the WheelEvent notification BEFORE we send the scroll delta. - Add two manual wpt tests to the `uievents/order-of-events` test collection --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `___` with appropriate data: --> - [X] `./mach build -d` does not report any errors - [X] `./mach test-tidy` does not report any errors - [X] These changes fix #22843 (GitHub issue number if applicable) <!-- Either: --> - [X] There are tests for these changes OR - [ ] These changes do not require tests because ___ <!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.--> <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/23154) <!-- Reviewable:end -->
This commit is contained in:
commit
973a3448a4
19 changed files with 536 additions and 146 deletions
|
@ -26,9 +26,11 @@ use num_traits::FromPrimitive;
|
|||
#[cfg(feature = "gl")]
|
||||
use pixels::PixelFormat;
|
||||
use profile_traits::time::{self as profile_time, profile, ProfilerCategory};
|
||||
use script_traits::CompositorEvent::{MouseButtonEvent, MouseMoveEvent, TouchEvent};
|
||||
use script_traits::CompositorEvent::{MouseButtonEvent, MouseMoveEvent, TouchEvent, WheelEvent};
|
||||
use script_traits::{AnimationState, AnimationTickType, ConstellationMsg, LayoutControlMsg};
|
||||
use script_traits::{MouseButton, MouseEventType, ScrollState, TouchEventType, TouchId};
|
||||
use script_traits::{
|
||||
MouseButton, MouseEventType, ScrollState, TouchEventType, TouchId, WheelDelta,
|
||||
};
|
||||
use script_traits::{UntrustedNodeAddress, WindowSizeData, WindowSizeType};
|
||||
use servo_geometry::DeviceIndependentPixel;
|
||||
use std::collections::HashMap;
|
||||
|
@ -792,6 +794,22 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn send_wheel_event(&mut self, delta: WheelDelta, point: DevicePoint) {
|
||||
let results = self.hit_test_at_point(point);
|
||||
if let Some(item) = results.items.first() {
|
||||
let event = WheelEvent(
|
||||
delta,
|
||||
item.point_in_viewport.to_untyped(),
|
||||
Some(UntrustedNodeAddress(item.tag.0 as *const c_void)),
|
||||
);
|
||||
let pipeline_id = PipelineId::from_webrender(item.pipeline);
|
||||
let msg = ConstellationMsg::ForwardEvent(pipeline_id, event);
|
||||
if let Err(e) = self.constellation_chan.send(msg) {
|
||||
warn!("Sending event to constellation failed ({:?}).", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn on_touch_event(
|
||||
&mut self,
|
||||
event_type: TouchEventType,
|
||||
|
@ -858,6 +876,10 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
|
|||
self.dispatch_mouse_window_event_class(MouseWindowEvent::Click(button, p));
|
||||
}
|
||||
|
||||
pub fn on_wheel_event(&mut self, delta: WheelDelta, p: DevicePoint) {
|
||||
self.send_wheel_event(delta, p);
|
||||
}
|
||||
|
||||
pub fn on_scroll_event(
|
||||
&mut self,
|
||||
delta: ScrollLocation,
|
||||
|
|
|
@ -10,7 +10,7 @@ use euclid::TypedScale;
|
|||
use gleam::gl;
|
||||
use keyboard_types::KeyboardEvent;
|
||||
use msg::constellation_msg::{PipelineId, TopLevelBrowsingContextId, TraversalDirection};
|
||||
use script_traits::{MouseButton, TouchEventType, TouchId};
|
||||
use script_traits::{MouseButton, TouchEventType, TouchId, WheelDelta};
|
||||
use servo_geometry::DeviceIndependentPixel;
|
||||
use servo_url::ServoUrl;
|
||||
use std::fmt::{Debug, Error, Formatter};
|
||||
|
@ -65,6 +65,8 @@ pub enum WindowEvent {
|
|||
MouseWindowMoveEventClass(DevicePoint),
|
||||
/// Touch event: type, identifier, point
|
||||
Touch(TouchEventType, TouchId, DevicePoint),
|
||||
/// Sent when user moves the mouse wheel.
|
||||
Wheel(WheelDelta, DevicePoint),
|
||||
/// Sent when the user scrolls. The first point is the delta and the second point is the
|
||||
/// origin.
|
||||
Scroll(ScrollLocation, DeviceIntPoint, TouchEventType),
|
||||
|
@ -113,6 +115,7 @@ impl Debug for WindowEvent {
|
|||
WindowEvent::MouseWindowEventClass(..) => write!(f, "Mouse"),
|
||||
WindowEvent::MouseWindowMoveEventClass(..) => write!(f, "MouseMove"),
|
||||
WindowEvent::Touch(..) => write!(f, "Touch"),
|
||||
WindowEvent::Wheel(..) => write!(f, "Wheel"),
|
||||
WindowEvent::Scroll(..) => write!(f, "Scroll"),
|
||||
WindowEvent::Zoom(..) => write!(f, "Zoom"),
|
||||
WindowEvent::PinchZoom(..) => write!(f, "PinchZoom"),
|
||||
|
|
|
@ -97,6 +97,7 @@ use crate::dom::treewalker::TreeWalker;
|
|||
use crate::dom::uievent::UIEvent;
|
||||
use crate::dom::virtualmethods::vtable_for;
|
||||
use crate::dom::webglcontextevent::WebGLContextEvent;
|
||||
use crate::dom::wheelevent::WheelEvent;
|
||||
use crate::dom::window::{ReflowReason, Window};
|
||||
use crate::dom::windowproxy::WindowProxy;
|
||||
use crate::fetch::FetchCanceller;
|
||||
|
@ -135,7 +136,9 @@ use profile_traits::time::{TimerMetadata, TimerMetadataFrameType, TimerMetadataR
|
|||
use ref_slice::ref_slice;
|
||||
use script_layout_interface::message::{Msg, ReflowGoal};
|
||||
use script_traits::{AnimationState, DocumentActivity, MouseButton, MouseEventType};
|
||||
use script_traits::{MsDuration, ScriptMsg, TouchEventType, TouchId, UntrustedNodeAddress};
|
||||
use script_traits::{
|
||||
MsDuration, ScriptMsg, TouchEventType, TouchId, UntrustedNodeAddress, WheelDelta,
|
||||
};
|
||||
use servo_arc::Arc;
|
||||
use servo_atoms::Atom;
|
||||
use servo_config::pref;
|
||||
|
@ -1218,6 +1221,53 @@ impl Document {
|
|||
.reflow(ReflowGoal::Full, ReflowReason::MouseEvent);
|
||||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
pub fn handle_wheel_event(
|
||||
&self,
|
||||
js_runtime: *mut JSRuntime,
|
||||
delta: WheelDelta,
|
||||
client_point: Point2D<f32>,
|
||||
node_address: Option<UntrustedNodeAddress>,
|
||||
) {
|
||||
let wheel_event_type_string = "wheel".to_owned();
|
||||
debug!("{}: at {:?}", wheel_event_type_string, client_point);
|
||||
|
||||
let el = node_address.and_then(|address| {
|
||||
let node = unsafe { node::from_untrusted_node_address(js_runtime, address) };
|
||||
node.inclusive_ancestors(ShadowIncluding::No)
|
||||
.filter_map(DomRoot::downcast::<Element>)
|
||||
.next()
|
||||
});
|
||||
|
||||
let el = match el {
|
||||
Some(el) => el,
|
||||
None => return,
|
||||
};
|
||||
|
||||
let node = el.upcast::<Node>();
|
||||
debug!("{}: on {:?}", wheel_event_type_string, node.debug_str());
|
||||
|
||||
// https://w3c.github.io/uievents/#event-wheelevents
|
||||
let event = WheelEvent::new(
|
||||
&self.window,
|
||||
DOMString::from(wheel_event_type_string),
|
||||
EventBubbles::Bubbles,
|
||||
EventCancelable::Cancelable,
|
||||
Some(&self.window),
|
||||
0i32,
|
||||
Finite::wrap(delta.x),
|
||||
Finite::wrap(delta.y),
|
||||
Finite::wrap(delta.z),
|
||||
delta.mode as u32,
|
||||
);
|
||||
|
||||
let event = event.upcast::<Event>();
|
||||
event.set_trusted(true);
|
||||
|
||||
let target = node.upcast();
|
||||
event.fire(target);
|
||||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
pub fn handle_touch_event(
|
||||
&self,
|
||||
|
|
|
@ -528,6 +528,7 @@ pub mod webgltexture;
|
|||
pub mod webgluniformlocation;
|
||||
pub mod webglvertexarrayobjectoes;
|
||||
pub mod websocket;
|
||||
pub mod wheelevent;
|
||||
pub mod window;
|
||||
pub mod windowproxy;
|
||||
pub mod worker;
|
||||
|
|
|
@ -38,7 +38,7 @@ pub struct MouseEvent {
|
|||
}
|
||||
|
||||
impl MouseEvent {
|
||||
fn new_inherited() -> MouseEvent {
|
||||
pub fn new_inherited() -> MouseEvent {
|
||||
MouseEvent {
|
||||
uievent: UIEvent::new_inherited(),
|
||||
screen_x: Cell::new(0),
|
||||
|
|
33
components/script/dom/webidls/WheelEvent.webidl
Normal file
33
components/script/dom/webidls/WheelEvent.webidl
Normal file
|
@ -0,0 +1,33 @@
|
|||
/* 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 https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// https://w3c.github.io/uievents/#interface-wheelevent
|
||||
[Constructor(DOMString typeArg, optional WheelEventInit wheelEventInitDict),
|
||||
Exposed=Window]
|
||||
interface WheelEvent : MouseEvent {
|
||||
const unsigned long DOM_DELTA_PIXEL = 0x00;
|
||||
const unsigned long DOM_DELTA_LINE = 0x01;
|
||||
const unsigned long DOM_DELTA_PAGE = 0x02;
|
||||
readonly attribute double deltaX;
|
||||
readonly attribute double deltaY;
|
||||
readonly attribute double deltaZ;
|
||||
readonly attribute unsigned long deltaMode;
|
||||
};
|
||||
|
||||
// https://w3c.github.io/uievents/#idl-wheeleventinit
|
||||
dictionary WheelEventInit : MouseEventInit {
|
||||
double deltaX = 0.0;
|
||||
double deltaY = 0.0;
|
||||
double deltaZ = 0.0;
|
||||
unsigned long deltaMode = 0;
|
||||
};
|
||||
|
||||
// https://w3c.github.io/uievents/#idl-interface-WheelEvent-initializers
|
||||
partial interface WheelEvent {
|
||||
// Deprecated in DOM Level 3
|
||||
void initWheelEvent (DOMString typeArg, boolean bubblesArg, boolean cancelableArg,
|
||||
Window? viewArg, long detailArg,
|
||||
double deltaX, double deltaY,
|
||||
double deltaZ, unsigned long deltaMode);
|
||||
};
|
163
components/script/dom/wheelevent.rs
Normal file
163
components/script/dom/wheelevent.rs
Normal file
|
@ -0,0 +1,163 @@
|
|||
/* 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 https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use crate::dom::bindings::codegen::Bindings::MouseEventBinding::MouseEventMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::WheelEventBinding;
|
||||
use crate::dom::bindings::codegen::Bindings::WheelEventBinding::WheelEventMethods;
|
||||
use crate::dom::bindings::error::Fallible;
|
||||
use crate::dom::bindings::inheritance::Castable;
|
||||
use crate::dom::bindings::num::Finite;
|
||||
use crate::dom::bindings::reflector::reflect_dom_object;
|
||||
use crate::dom::bindings::root::DomRoot;
|
||||
use crate::dom::bindings::str::DOMString;
|
||||
use crate::dom::event::{Event, EventBubbles, EventCancelable};
|
||||
use crate::dom::mouseevent::MouseEvent;
|
||||
use crate::dom::window::Window;
|
||||
use dom_struct::dom_struct;
|
||||
use std::cell::Cell;
|
||||
|
||||
#[dom_struct]
|
||||
pub struct WheelEvent {
|
||||
mouseevent: MouseEvent,
|
||||
delta_x: Cell<Finite<f64>>,
|
||||
delta_y: Cell<Finite<f64>>,
|
||||
delta_z: Cell<Finite<f64>>,
|
||||
delta_mode: Cell<u32>,
|
||||
}
|
||||
|
||||
impl WheelEvent {
|
||||
fn new_inherited() -> WheelEvent {
|
||||
WheelEvent {
|
||||
mouseevent: MouseEvent::new_inherited(),
|
||||
delta_x: Cell::new(Finite::wrap(0.0)),
|
||||
delta_y: Cell::new(Finite::wrap(0.0)),
|
||||
delta_z: Cell::new(Finite::wrap(0.0)),
|
||||
delta_mode: Cell::new(0),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_unintialized(window: &Window) -> DomRoot<WheelEvent> {
|
||||
reflect_dom_object(
|
||||
Box::new(WheelEvent::new_inherited()),
|
||||
window,
|
||||
WheelEventBinding::Wrap,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new(
|
||||
window: &Window,
|
||||
type_: DOMString,
|
||||
can_bubble: EventBubbles,
|
||||
cancelable: EventCancelable,
|
||||
view: Option<&Window>,
|
||||
detail: i32,
|
||||
delta_x: Finite<f64>,
|
||||
delta_y: Finite<f64>,
|
||||
delta_z: Finite<f64>,
|
||||
delta_mode: u32,
|
||||
) -> DomRoot<WheelEvent> {
|
||||
let ev = WheelEvent::new_unintialized(window);
|
||||
ev.InitWheelEvent(
|
||||
type_,
|
||||
bool::from(can_bubble),
|
||||
bool::from(cancelable),
|
||||
view,
|
||||
detail,
|
||||
delta_x,
|
||||
delta_y,
|
||||
delta_z,
|
||||
delta_mode,
|
||||
);
|
||||
|
||||
ev
|
||||
}
|
||||
|
||||
pub fn Constructor(
|
||||
window: &Window,
|
||||
type_: DOMString,
|
||||
init: &WheelEventBinding::WheelEventInit,
|
||||
) -> Fallible<DomRoot<WheelEvent>> {
|
||||
let event = WheelEvent::new(
|
||||
window,
|
||||
type_,
|
||||
EventBubbles::from(init.parent.parent.parent.parent.bubbles),
|
||||
EventCancelable::from(init.parent.parent.parent.parent.cancelable),
|
||||
init.parent.parent.parent.view.deref(),
|
||||
init.parent.parent.parent.detail,
|
||||
init.deltaX,
|
||||
init.deltaY,
|
||||
init.deltaZ,
|
||||
init.deltaMode,
|
||||
);
|
||||
|
||||
Ok(event)
|
||||
}
|
||||
}
|
||||
|
||||
impl WheelEventMethods for WheelEvent {
|
||||
// https://w3c.github.io/uievents/#widl-WheelEvent-deltaX
|
||||
fn DeltaX(&self) -> Finite<f64> {
|
||||
self.delta_x.get()
|
||||
}
|
||||
|
||||
// https://w3c.github.io/uievents/#widl-WheelEvent-deltaY
|
||||
fn DeltaY(&self) -> Finite<f64> {
|
||||
self.delta_y.get()
|
||||
}
|
||||
|
||||
// https://w3c.github.io/uievents/#widl-WheelEvent-deltaZ
|
||||
fn DeltaZ(&self) -> Finite<f64> {
|
||||
self.delta_z.get()
|
||||
}
|
||||
|
||||
// https://w3c.github.io/uievents/#widl-WheelEvent-deltaMode
|
||||
fn DeltaMode(&self) -> u32 {
|
||||
self.delta_mode.get()
|
||||
}
|
||||
|
||||
// https://w3c.github.io/uievents/#widl-WheelEvent-initWheelEvent
|
||||
fn InitWheelEvent(
|
||||
&self,
|
||||
type_arg: DOMString,
|
||||
can_bubble_arg: bool,
|
||||
cancelable_arg: bool,
|
||||
view_arg: Option<&Window>,
|
||||
detail_arg: i32,
|
||||
delta_x_arg: Finite<f64>,
|
||||
delta_y_arg: Finite<f64>,
|
||||
delta_z_arg: Finite<f64>,
|
||||
delta_mode_arg: u32,
|
||||
) {
|
||||
if self.upcast::<Event>().dispatching() {
|
||||
return;
|
||||
}
|
||||
|
||||
self.upcast::<MouseEvent>().InitMouseEvent(
|
||||
type_arg,
|
||||
can_bubble_arg,
|
||||
cancelable_arg,
|
||||
view_arg,
|
||||
detail_arg,
|
||||
self.mouseevent.ScreenX(),
|
||||
self.mouseevent.ScreenY(),
|
||||
self.mouseevent.ClientX(),
|
||||
self.mouseevent.ClientY(),
|
||||
self.mouseevent.CtrlKey(),
|
||||
self.mouseevent.AltKey(),
|
||||
self.mouseevent.ShiftKey(),
|
||||
self.mouseevent.MetaKey(),
|
||||
self.mouseevent.Button(),
|
||||
self.mouseevent.GetRelatedTarget().deref(),
|
||||
);
|
||||
self.delta_x.set(delta_x_arg);
|
||||
self.delta_y.set(delta_y_arg);
|
||||
self.delta_z.set(delta_z_arg);
|
||||
self.delta_mode.set(delta_mode_arg);
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-event-istrusted
|
||||
fn IsTrusted(&self) -> bool {
|
||||
self.mouseevent.IsTrusted()
|
||||
}
|
||||
}
|
|
@ -125,6 +125,7 @@ use script_layout_interface::message::{self, LayoutThreadInit, Msg, ReflowGoal};
|
|||
use script_traits::webdriver_msg::WebDriverScriptCommand;
|
||||
use script_traits::CompositorEvent::{
|
||||
CompositionEvent, KeyboardEvent, MouseButtonEvent, MouseMoveEvent, ResizeEvent, TouchEvent,
|
||||
WheelEvent,
|
||||
};
|
||||
use script_traits::{CompositorEvent, ConstellationControlMsg};
|
||||
use script_traits::{DiscardBrowsingContext, DocumentActivity, EventResult};
|
||||
|
@ -132,7 +133,7 @@ use script_traits::{InitialScriptState, JsEvalResult, LayoutMsg, LoadData};
|
|||
use script_traits::{MouseButton, MouseEventType, NewLayoutInfo};
|
||||
use script_traits::{Painter, ProgressiveWebMetricType, ScriptMsg, ScriptThreadFactory};
|
||||
use script_traits::{ScriptToConstellationChan, TimerEvent, TimerSchedulerMsg};
|
||||
use script_traits::{TimerSource, TouchEventType, TouchId, UntrustedNodeAddress};
|
||||
use script_traits::{TimerSource, TouchEventType, TouchId, UntrustedNodeAddress, WheelDelta};
|
||||
use script_traits::{UpdatePipelineIdReason, WindowSizeData, WindowSizeType};
|
||||
use servo_atoms::Atom;
|
||||
use servo_config::opts;
|
||||
|
@ -3162,6 +3163,10 @@ impl ScriptThread {
|
|||
}
|
||||
},
|
||||
|
||||
WheelEvent(delta, point, node_address) => {
|
||||
self.handle_wheel_event(pipeline_id, delta, point, node_address);
|
||||
},
|
||||
|
||||
KeyboardEvent(key_event) => {
|
||||
let document = match { self.documents.borrow().find_document(pipeline_id) } {
|
||||
Some(document) => document,
|
||||
|
@ -3229,6 +3234,20 @@ impl ScriptThread {
|
|||
)
|
||||
}
|
||||
|
||||
fn handle_wheel_event(
|
||||
&self,
|
||||
pipeline_id: PipelineId,
|
||||
wheel_delta: WheelDelta,
|
||||
point: Point2D<f32>,
|
||||
node_address: Option<UntrustedNodeAddress>,
|
||||
) {
|
||||
let document = match { self.documents.borrow().find_document(pipeline_id) } {
|
||||
Some(document) => document,
|
||||
None => return warn!("Message sent to closed pipeline {}.", pipeline_id),
|
||||
};
|
||||
document.handle_wheel_event(self.js_runtime.rt(), wheel_delta, point, node_address);
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#navigating-across-documents>
|
||||
/// The entry point for content to notify that a new load has been requested
|
||||
/// for the given pipeline (specifically the "navigate" algorithm).
|
||||
|
|
|
@ -462,6 +462,30 @@ pub enum MouseEventType {
|
|||
MouseUp,
|
||||
}
|
||||
|
||||
/// Mode to measure WheelDelta floats in
|
||||
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
|
||||
pub enum WheelMode {
|
||||
/// Delta values are specified in pixels
|
||||
DeltaPixel = 0x00,
|
||||
/// Delta values are specified in lines
|
||||
DeltaLine = 0x01,
|
||||
/// Delta values are specified in pages
|
||||
DeltaPage = 0x02,
|
||||
}
|
||||
|
||||
/// The Wheel event deltas in every direction
|
||||
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
|
||||
pub struct WheelDelta {
|
||||
/// Delta in the left/right direction
|
||||
pub x: f64,
|
||||
/// Delta in the up/down direction
|
||||
pub y: f64,
|
||||
/// Delta in the direction going into/out of the screen
|
||||
pub z: f64,
|
||||
/// Mode to measure the floats in
|
||||
pub mode: WheelMode,
|
||||
}
|
||||
|
||||
/// Events from the compositor that the script thread needs to know about
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub enum CompositorEvent {
|
||||
|
@ -491,6 +515,8 @@ pub enum CompositorEvent {
|
|||
Point2D<f32>,
|
||||
Option<UntrustedNodeAddress>,
|
||||
),
|
||||
/// A wheel event was generated with a delta in the X, Y, and/or Z directions
|
||||
WheelEvent(WheelDelta, Point2D<f32>, Option<UntrustedNodeAddress>),
|
||||
/// A key was pressed.
|
||||
KeyboardEvent(KeyboardEvent),
|
||||
/// An event from the IME is dispatched.
|
||||
|
|
|
@ -397,6 +397,10 @@ where
|
|||
.on_touch_event(event_type, identifier, location);
|
||||
},
|
||||
|
||||
WindowEvent::Wheel(delta, location) => {
|
||||
self.compositor.on_wheel_event(delta, location);
|
||||
},
|
||||
|
||||
WindowEvent::Scroll(delta, cursor, phase) => {
|
||||
self.compositor.on_scroll_event(delta, cursor, phase);
|
||||
},
|
||||
|
|
|
@ -23,7 +23,7 @@ use keyboard_types::{Key, KeyState, KeyboardEvent};
|
|||
use servo::compositing::windowing::{AnimationState, MouseWindowEvent, WindowEvent};
|
||||
use servo::compositing::windowing::{EmbedderCoordinates, WindowMethods};
|
||||
use servo::embedder_traits::Cursor;
|
||||
use servo::script_traits::TouchEventType;
|
||||
use servo::script_traits::{TouchEventType, WheelMode, WheelDelta};
|
||||
use servo::servo_config::opts;
|
||||
use servo::servo_geometry::DeviceIndependentPixel;
|
||||
use servo::style_traits::DevicePixel;
|
||||
|
@ -400,14 +400,22 @@ impl WindowPortsMethods for Window {
|
|||
)));
|
||||
},
|
||||
glutin::WindowEvent::MouseWheel { delta, phase, .. } => {
|
||||
let (mut dx, mut dy) = match delta {
|
||||
MouseScrollDelta::LineDelta(dx, dy) => (dx, dy * LINE_HEIGHT),
|
||||
let (mut dx, mut dy, mode) = match delta {
|
||||
MouseScrollDelta::LineDelta(dx, dy) => (dx as f64, (dy * LINE_HEIGHT) as f64,
|
||||
WheelMode::DeltaLine),
|
||||
MouseScrollDelta::PixelDelta(position) => {
|
||||
let position =
|
||||
position.to_physical(self.device_hidpi_factor().get() as f64);
|
||||
(position.x as f32, position.y as f32)
|
||||
(position.x as f64, position.y as f64, WheelMode::DeltaPixel)
|
||||
},
|
||||
};
|
||||
|
||||
// Create wheel event before snapping to the major axis of movement
|
||||
let wheel_delta = WheelDelta { x: dx, y: dy, z: 0.0, mode };
|
||||
let pos = self.mouse_pos.get();
|
||||
let position = TypedPoint2D::new(pos.x as f32, pos.y as f32);
|
||||
let wheel_event = WindowEvent::Wheel(wheel_delta, position);
|
||||
|
||||
// Scroll events snap to the major axis of movement, with vertical
|
||||
// preferred over horizontal.
|
||||
if dy.abs() >= dx.abs() {
|
||||
|
@ -416,10 +424,13 @@ impl WindowPortsMethods for Window {
|
|||
dy = 0.0;
|
||||
}
|
||||
|
||||
let scroll_location = ScrollLocation::Delta(TypedVector2D::new(dx, dy));
|
||||
let scroll_location = ScrollLocation::Delta(TypedVector2D::new(dx as f32, dy as f32));
|
||||
let phase = winit_phase_to_touch_event_type(phase);
|
||||
let event = WindowEvent::Scroll(scroll_location, self.mouse_pos.get(), phase);
|
||||
self.event_queue.borrow_mut().push(event);
|
||||
let scroll_event = WindowEvent::Scroll(scroll_location, self.mouse_pos.get(), phase);
|
||||
|
||||
// Send events
|
||||
self.event_queue.borrow_mut().push(wheel_event);
|
||||
self.event_queue.borrow_mut().push(scroll_event);
|
||||
},
|
||||
glutin::WindowEvent::Touch(touch) => {
|
||||
use servo::script_traits::TouchId;
|
||||
|
|
|
@ -18085,6 +18085,18 @@
|
|||
{}
|
||||
]
|
||||
],
|
||||
"uievents/order-of-events/mouse-events/wheel-basic-manual.html": [
|
||||
[
|
||||
"uievents/order-of-events/mouse-events/wheel-basic-manual.html",
|
||||
{}
|
||||
]
|
||||
],
|
||||
"uievents/order-of-events/mouse-events/wheel-scrolling-manual.html": [
|
||||
[
|
||||
"uievents/order-of-events/mouse-events/wheel-scrolling-manual.html",
|
||||
{}
|
||||
]
|
||||
],
|
||||
"vibration/cancel-when-hidden-manual.html": [
|
||||
[
|
||||
"vibration/cancel-when-hidden-manual.html",
|
||||
|
@ -671723,6 +671735,14 @@
|
|||
"1d96fd303adce6efd9900bd19e869326d968c763",
|
||||
"manual"
|
||||
],
|
||||
"uievents/order-of-events/mouse-events/wheel-basic-manual.html": [
|
||||
"093951dfe49b4f68e9015614f320f9254d54f391",
|
||||
"manual"
|
||||
],
|
||||
"uievents/order-of-events/mouse-events/wheel-scrolling-manual.html": [
|
||||
"5acae6d4777ccff91d4bafcfa16cdde2eba69912",
|
||||
"manual"
|
||||
],
|
||||
"uievents/resources/eventrecorder.js": [
|
||||
"649930b42cbaaedbfab8341df90da90fff7b2ff1",
|
||||
"support"
|
||||
|
|
|
@ -3,21 +3,6 @@
|
|||
[MouseEvent constructor (argument with non-default values)]
|
||||
expected: FAIL
|
||||
|
||||
[WheelEvent constructor (no argument)]
|
||||
expected: FAIL
|
||||
|
||||
[WheelEvent constructor (undefined argument)]
|
||||
expected: FAIL
|
||||
|
||||
[WheelEvent constructor (null argument)]
|
||||
expected: FAIL
|
||||
|
||||
[WheelEvent constructor (empty argument)]
|
||||
expected: FAIL
|
||||
|
||||
[WheelEvent constructor (argument with default values)]
|
||||
expected: FAIL
|
||||
|
||||
[WheelEvent constructor (argument with non-default values)]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -3,9 +3,6 @@
|
|||
[Untitled]
|
||||
expected: FAIL
|
||||
|
||||
[Constructed WheelEvent timestamp should be high resolution and have the same time origin as performance.now()]
|
||||
expected: FAIL
|
||||
|
||||
[Constructed GamepadEvent timestamp should be high resolution and have the same time origin as performance.now()]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -2,18 +2,9 @@
|
|||
[idlharness]
|
||||
expected: FAIL
|
||||
|
||||
[MouseEvent interface: new WheelEvent("event") must inherit property "buttons" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
[UIEvent interface: new CompositionEvent("event") must inherit property "view" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
[WheelEvent interface object length]
|
||||
expected: FAIL
|
||||
|
||||
[WheelEvent interface object name]
|
||||
expected: FAIL
|
||||
|
||||
[UIEvent interface: new CompositionEvent("event") must inherit property "which" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -32,27 +23,9 @@
|
|||
[MouseEvent interface: operation getModifierState(DOMString)]
|
||||
expected: FAIL
|
||||
|
||||
[WheelEvent interface: existence and properties of interface prototype object]
|
||||
expected: FAIL
|
||||
|
||||
[MouseEvent interface: new WheelEvent("event") must inherit property "shiftKey" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
[CompositionEvent interface object name]
|
||||
expected: FAIL
|
||||
|
||||
[UIEvent interface: new WheelEvent("event") must inherit property "detail" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
[WheelEvent interface: constant DOM_DELTA_PAGE on interface prototype object]
|
||||
expected: FAIL
|
||||
|
||||
[MouseEvent interface: new WheelEvent("event") must inherit property "ctrlKey" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
[WheelEvent interface: constant DOM_DELTA_PAGE on interface object]
|
||||
expected: FAIL
|
||||
|
||||
[MouseEvent interface: new MouseEvent("event") must inherit property "getModifierState(DOMString)" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -62,57 +35,18 @@
|
|||
[CompositionEvent interface: existence and properties of interface object]
|
||||
expected: FAIL
|
||||
|
||||
[WheelEvent interface: constant DOM_DELTA_PIXEL on interface object]
|
||||
expected: FAIL
|
||||
|
||||
[WheelEvent interface: new WheelEvent("event") must inherit property "DOM_DELTA_LINE" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
[UIEvent interface: new WheelEvent("event") must inherit property "view" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
[MouseEvent interface: new WheelEvent("event") must inherit property "altKey" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
[UIEvent interface: new CompositionEvent("event") must inherit property "detail" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
[MouseEvent interface: new WheelEvent("event") must inherit property "relatedTarget" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
[MouseEvent interface: calling getModifierState(DOMString) on new MouseEvent("event") with too few arguments must throw TypeError]
|
||||
expected: FAIL
|
||||
|
||||
[WheelEvent interface: existence and properties of interface object]
|
||||
expected: FAIL
|
||||
|
||||
[WheelEvent interface: attribute deltaX]
|
||||
expected: FAIL
|
||||
|
||||
[WheelEvent interface: attribute deltaY]
|
||||
expected: FAIL
|
||||
|
||||
[WheelEvent interface: attribute deltaZ]
|
||||
expected: FAIL
|
||||
|
||||
[WheelEvent must be primary interface of new WheelEvent("event")]
|
||||
expected: FAIL
|
||||
|
||||
[WheelEvent interface: constant DOM_DELTA_LINE on interface object]
|
||||
expected: FAIL
|
||||
|
||||
[Stringification of new CompositionEvent("event")]
|
||||
expected: FAIL
|
||||
|
||||
[WheelEvent interface: new WheelEvent("event") must inherit property "deltaX" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
[MouseEvent interface: new WheelEvent("event") must inherit property "getModifierState(DOMString)" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
[MouseEvent interface: new WheelEvent("event") must inherit property "metaKey" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
[CompositionEvent interface: existence and properties of interface prototype object]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -122,69 +56,21 @@
|
|||
[CompositionEvent must be primary interface of new CompositionEvent("event")]
|
||||
expected: FAIL
|
||||
|
||||
[WheelEvent interface: new WheelEvent("event") must inherit property "DOM_DELTA_PIXEL" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
[Stringification of new WheelEvent("event")]
|
||||
expected: FAIL
|
||||
|
||||
[UIEvent interface: new FocusEvent("event") must inherit property "which" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
[MouseEvent interface: new WheelEvent("event") must inherit property "screenX" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
[CompositionEvent interface: new CompositionEvent("event") must inherit property "data" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
[WheelEvent interface: existence and properties of interface prototype object's "constructor" property]
|
||||
expected: FAIL
|
||||
|
||||
[MouseEvent interface: new WheelEvent("event") must inherit property "clientX" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
[CompositionEvent interface: existence and properties of interface prototype object's @@unscopables property]
|
||||
expected: FAIL
|
||||
|
||||
[WheelEvent interface: existence and properties of interface prototype object's @@unscopables property]
|
||||
expected: FAIL
|
||||
|
||||
[MouseEvent interface: new WheelEvent("event") must inherit property "screenY" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
[WheelEvent interface: attribute deltaMode]
|
||||
expected: FAIL
|
||||
|
||||
[WheelEvent interface: constant DOM_DELTA_LINE on interface prototype object]
|
||||
expected: FAIL
|
||||
|
||||
[CompositionEvent interface: existence and properties of interface prototype object's "constructor" property]
|
||||
expected: FAIL
|
||||
|
||||
[WheelEvent interface: new WheelEvent("event") must inherit property "DOM_DELTA_PAGE" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
[MouseEvent interface: new WheelEvent("event") must inherit property "button" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
[MouseEvent interface: new WheelEvent("event") must inherit property "clientY" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
[WheelEvent interface: new WheelEvent("event") must inherit property "deltaMode" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
[UIEvent interface: attribute which]
|
||||
expected: FAIL
|
||||
|
||||
[WheelEvent interface: new WheelEvent("event") must inherit property "deltaY" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
[WheelEvent interface: constant DOM_DELTA_PIXEL on interface prototype object]
|
||||
expected: FAIL
|
||||
|
||||
[WheelEvent interface: new WheelEvent("event") must inherit property "deltaZ" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
[CompositionEvent interface object length]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -20330,7 +20330,7 @@
|
|||
"testharness"
|
||||
],
|
||||
"mozilla/interfaces.html": [
|
||||
"2effd46f565c4787e8632f5e898e1b43d457672f",
|
||||
"b6cbf4b4f9b4fdcbe5f05a96970822d2ae9d325d",
|
||||
"testharness"
|
||||
],
|
||||
"mozilla/interfaces.js": [
|
||||
|
|
|
@ -242,6 +242,7 @@ test_interfaces([
|
|||
"WebGLActiveInfo",
|
||||
"WebGLShaderPrecisionFormat",
|
||||
"WebSocket",
|
||||
"WheelEvent",
|
||||
"Window",
|
||||
"Worker",
|
||||
"XMLDocument",
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
<!doctype html>
|
||||
<head>
|
||||
<meta charset=utf-8>
|
||||
<title>WheelEvent - basic wheel event</title>
|
||||
<style>
|
||||
.testarea{ margin: auto; width: 80%; height: 250px; border: 1px solid grey; position: relative; }
|
||||
|
||||
#wheelbox, #scrollbox { background-color: red; border: 1px solid black; margin: 0; padding: 0; }
|
||||
#wheelbox.green, #scrollbox.green { background-color: green; }
|
||||
#wheelbox { position: absolute; left: 15%; top: 15%; width: 50%; height: 50% }
|
||||
#scrollbox { position: absolute; right: 15%; bottom: 15%; width: 50%; height: 50% }
|
||||
</style>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script>
|
||||
setup({explicit_timeout: true});
|
||||
</script>
|
||||
<script src="/uievents/resources/eventrecorder.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<p><strong>Description</strong>: Verifies that wheel events are captured and sent</p>
|
||||
<p><strong>Instructions</strong>: </p>
|
||||
<ol>
|
||||
<li>Place your mouse pointer over the top box</li>
|
||||
<li>Scroll the mouse wheel to change the box color</li>
|
||||
<li>Move the mouse pointer to the second box</li>
|
||||
<li>Scroll the mouse wheel again to change this box's color</li>
|
||||
</ol>
|
||||
<p><strong>Test Passes</strong> if the box turns green and the word 'PASS' appears below</p>
|
||||
|
||||
<section class="testarea">
|
||||
<div id="scrollbox"></div>
|
||||
<div id="wheelbox"></div>
|
||||
</section>
|
||||
|
||||
<script>
|
||||
var wheelbox = document.getElementById("wheelbox");
|
||||
var scrollbox = document.getElementById("scrollbox");
|
||||
|
||||
EventRecorder.configure({
|
||||
mergeEventTypes: ['wheel'],
|
||||
objectMap: {
|
||||
"div#wheelbox": wheelbox,
|
||||
"div#scrollbox": scrollbox
|
||||
}
|
||||
});
|
||||
|
||||
wheelbox.addRecordedEventListener('wheel', function (e) {
|
||||
e.stopPropagation();
|
||||
this.className = "green";
|
||||
});
|
||||
|
||||
scrollbox.addRecordedEventListener('wheel', function (e) {
|
||||
e.stopPropagation();
|
||||
this.className = "green";
|
||||
endTest();
|
||||
done();
|
||||
});
|
||||
|
||||
function endTest() {
|
||||
EventRecorder.stop();
|
||||
var results = EventRecorder.getRecords();
|
||||
// Check results:
|
||||
assert_equals(results.length, 2, "Two mousemove events");
|
||||
assert_equals(results[0].event.type, 'wheel', "First event is a wheel event");
|
||||
assert_equals(results[1].event.type, 'wheel', "Second event is a wheel event");
|
||||
assert_equals(results[0].event.target, 'div#wheelbox', "First event targetted wheelbox");
|
||||
assert_equals(results[1].event.target, 'div#scrollbox', "Second event targetted scrollbox");
|
||||
}
|
||||
|
||||
EventRecorder.start();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,94 @@
|
|||
<!doctype html>
|
||||
<head>
|
||||
<meta charset=utf-8>
|
||||
<title>WheelEvent - scrolling wheel event</title>
|
||||
<style>
|
||||
.testarea{ margin: auto; width: 80%; height: 250px; border: 1px solid grey; position: relative; }
|
||||
|
||||
#wheelbox { background-color: red; border: 1px solid black; margin: 0; padding: 0; }
|
||||
#wheelbox.green { background-color: green; }
|
||||
#wheelbox { position: absolute; left: 15%; top: 15%; width: 50%; height: 50% }
|
||||
|
||||
#lipsum { margin: auto; width: 40%; position: relative; }
|
||||
</style>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script>
|
||||
setup({explicit_timeout: true});
|
||||
</script>
|
||||
<script src="/uievents/resources/eventrecorder.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<p><strong>Description</strong>: Verifies that we can scroll the page when wheel events are captured</p>
|
||||
<p><strong>Instructions</strong>: </p>
|
||||
<ol>
|
||||
<li>Place your mouse over the box</li>
|
||||
<li>Scroll the mouse wheel to change the box color</li>
|
||||
</ol>
|
||||
<p><strong>Test Passes</strong> if the box turns green and the word 'PASS' appears below</p>
|
||||
|
||||
<section class="testarea">
|
||||
<div id="wheelbox"></div>
|
||||
</section>
|
||||
|
||||
<script>
|
||||
var wheelbox = document.getElementById("wheelbox");
|
||||
|
||||
EventRecorder.configure({
|
||||
mergeEventTypes: ['wheel'],
|
||||
objectMap: {
|
||||
"div#wheelbox": wheelbox
|
||||
}
|
||||
});
|
||||
|
||||
wheelbox.addRecordedEventListener('wheel', function (e) {
|
||||
e.stopPropagation();
|
||||
this.className = "green";
|
||||
endTest();
|
||||
done();
|
||||
});
|
||||
|
||||
function endTest() {
|
||||
EventRecorder.stop();
|
||||
var results = EventRecorder.getRecords();
|
||||
// Check results:
|
||||
assert_equals(results.length, 1, "One mousemove event");
|
||||
assert_equals(results[0].event.type, 'wheel', "First event is a wheel event");
|
||||
assert_equals(results[0].event.target, 'div#wheelbox', "First event targetted wheelbox");
|
||||
}
|
||||
|
||||
EventRecorder.start();
|
||||
</script>
|
||||
|
||||
<section id="lipsum">
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras non sodales nunc. Nunc interdum laoreet magna, quis faucibus mi malesuada ut. Donec eu dictum nibh. Quisque consectetur velit arcu, ac bibendum lacus interdum eget. Sed gravida consequat lectus feugiat feugiat. Nunc finibus lacus sit amet ultrices pulvinar. Integer ultrices nulla ut ligula porttitor aliquet. Nulla dapibus dignissim cursus. Morbi euismod accumsan dapibus. Nullam rutrum neque eu finibus luctus. Praesent ultricies pellentesque bibendum.
|
||||
</p>
|
||||
<p>
|
||||
Cras faucibus facilisis justo, sit amet euismod nulla posuere sit amet. Donec odio justo, egestas ornare tristique vitae, convallis ac diam. Cras rutrum, massa nec feugiat sodales, diam ex faucibus diam, ac imperdiet dolor lacus eu nisi. Fusce feugiat ex vitae ex fermentum convallis. Pellentesque vulputate cursus lorem, vel sollicitudin eros feugiat a. Morbi egestas neque erat, eget semper nisi tempor id. Etiam eleifend fermentum convallis. Proin sem ipsum, porttitor a condimentum vel, malesuada ac est.
|
||||
</p>
|
||||
<p>
|
||||
Curabitur at porttitor ipsum, nec aliquam sapien. Quisque aliquet dapibus nulla. Donec consequat ornare dui, quis efficitur metus fringilla vitae. Fusce ut ultricies neque. In rutrum efficitur mi ut rhoncus. In ornare, justo quis volutpat dapibus, nulla ligula tincidunt dui, vitae porttitor lectus est eget metus. Integer convallis leo vitae dui auctor, at consectetur sem sollicitudin. Ut condimentum enim non tellus finibus mattis.
|
||||
</p>
|
||||
<p>
|
||||
Aenean pharetra, erat rutrum lacinia iaculis, ligula quam efficitur nibh, sit amet porttitor est ligula semper sapien. Morbi cursus finibus justo blandit commodo. Pellentesque at diam scelerisque, varius dolor a, tempus tortor. Nam placerat mi id elit eleifend scelerisque. Fusce imperdiet ac augue id fringilla. Quisque luctus nec sapien in tempor. Vestibulum faucibus metus nulla, nec faucibus metus rutrum sit amet. Nulla facilisi. Aliquam varius dignissim laoreet. Morbi gravida, velit sed efficitur iaculis, enim lectus hendrerit diam, ac aliquam mauris enim ut nulla. Vivamus quis aliquet libero. Vivamus accumsan elit et dolor posuere, ac volutpat nulla blandit. Proin vitae tortor rutrum, hendrerit odio vitae, ultricies metus. Pellentesque mattis sem ac lorem lacinia vestibulum. Ut orci est, placerat vitae imperdiet ac, dictum a nisi.
|
||||
</p>
|
||||
<p>
|
||||
Proin elementum faucibus neque, sed varius est rhoncus nec. Ut nec porttitor velit, a viverra lorem. Nam lectus arcu, malesuada non suscipit at, efficitur eget diam. Morbi at purus vitae nisl mollis suscipit ac at dolor. Nam vel ullamcorper est. Sed efficitur ligula quis elit viverra, at ornare velit posuere. Maecenas porta risus velit, eu pharetra ex vehicula rutrum. Mauris eu tortor vestibulum, tristique arcu et, euismod velit. Donec et mattis ligula. Pellentesque lacinia elit sit amet libero rhoncus convallis. Aenean porta, enim eget consequat blandit, leo ante finibus massa, gravida viverra dolor massa porta neque.
|
||||
</p>
|
||||
<p>
|
||||
Mauris vitae tortor quis nibh tempus tempor et quis eros. Quisque massa libero, pulvinar nec fringilla non, molestie eget justo. Maecenas mollis est et felis auctor, eu sodales erat fermentum. Curabitur porttitor nibh magna, non porta mi dictum et. Aliquam nec auctor nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi nec porttitor ante, a rutrum ante. Vivamus faucibus et augue sit amet auctor. Nulla nec risus lacus. Quisque mattis vitae neque sit amet aliquet. Nam placerat mattis iaculis.
|
||||
</p>
|
||||
<p>
|
||||
Quisque ultrices varius nisi eu feugiat. Sed a aliquam risus. Nulla fermentum erat odio, ultricies fermentum arcu tristique a. Phasellus vulputate sapien in augue sollicitudin auctor. Aenean eu lectus placerat, bibendum leo et, efficitur diam. Quisque in lacus in justo volutpat convallis. Sed vitae rhoncus tellus. Curabitur elementum mi id diam volutpat interdum. Proin urna orci, vehicula non mattis dictum, dignissim a sem. Vivamus in ultricies quam, id auctor arcu. In lacinia ex ac dui fermentum pulvinar. Donec luctus auctor ultrices. Suspendisse semper eros a dolor vulputate aliquam.
|
||||
</p>
|
||||
<p>
|
||||
Nullam in tempor tellus, ac ullamcorper tortor. Cras vel aliquet magna, in congue nisi. Nam turpis quam, consectetur at vehicula ut, pellentesque eu risus. In vestibulum neque a ex fringilla tempor quis ac dui. Praesent dictum venenatis scelerisque. Proin ut lectus lobortis, eleifend ipsum blandit, tristique mi. Etiam lacus est, scelerisque at lectus at, interdum mattis ex. Nulla neque mauris, suscipit tempus elementum quis, viverra eu ligula. Etiam at velit vulputate, pulvinar metus eu, hendrerit odio. Sed nec feugiat tortor. Fusce nunc sapien, pretium non viverra et, volutpat eu arcu. Nullam libero justo, varius at suscipit at, scelerisque ut lectus. In leo lorem, tempor sed pellentesque nec, vulputate non lectus. Pellentesque volutpat sit amet lectus in finibus. Aliquam tristique, arcu sit amet venenatis commodo, ligula dolor efficitur ante, at faucibus mi diam ac sapien.
|
||||
</p>
|
||||
<p>
|
||||
Ut porttitor metus et mauris euismod iaculis. Ut in lorem neque. Nam a sollicitudin ligula, non lobortis sapien. Curabitur ullamcorper justo quis vulputate cursus. Aliquam rhoncus volutpat quam non condimentum. Donec dictum aliquam metus tempor vehicula. Curabitur lorem urna, venenatis vel tellus et, mollis ornare sapien. Aliquam erat elit, tristique at suscipit efficitur, condimentum eget quam. Quisque vel metus nisl. Integer eget cursus neque, sed auctor purus. Sed ac urna nec lectus elementum laoreet. Donec posuere diam ut nibh vestibulum efficitur. Nam ut rhoncus enim, semper sollicitudin libero.
|
||||
</p>
|
||||
</section>
|
||||
</body>
|
||||
</html>
|
Loading…
Add table
Add a link
Reference in a new issue