forked from mirrors/gecko-dev
		
	 9118aca3be
			
		
	
	
		9118aca3be
		
	
	
	
	
		
			
			<!-- Please describe your changes on the following line: --> Added toggling of active state for element and parents on mousedown/mouseup. Active state is removed when mouseout. (hover) - As with my other PR i'm struggling a bit with the automated testing. I've added a manual test case and found quirks-mode/active-and-hover-manual.html which - aside from also being a manual test, is functional in Firefox but does not render correctly in Servo. - Not implemented: In Firefox, behaviour differs with a <!DOCTYPE HTML> and an anchor does not lose it's activation on mouseout; whereas a button does. --- <!-- 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 #8719 (github issue number if applicable). <!-- Either: --> - [ ] There are tests for these changes OR - [ ] These changes do not require tests because _____ <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> Source-Repo: https://github.com/servo/servo Source-Revision: 5afdf7fb5c9a4c997a287f6d61ec05857f073ce2
		
			
				
	
	
		
			120 lines
		
	
	
	
		
			4.1 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			120 lines
		
	
	
	
		
			4.1 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| /* 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::EventMethods;
 | |
| use dom::bindings::inheritance::Castable;
 | |
| use dom::bindings::str::DOMString;
 | |
| use dom::element::Element;
 | |
| use dom::event::{Event, EventBubbles, EventCancelable};
 | |
| use dom::eventtarget::EventTarget;
 | |
| use dom::mouseevent::MouseEvent;
 | |
| use dom::node::window_from_node;
 | |
| use dom::window::ReflowReason;
 | |
| use script_layout_interface::message::ReflowQueryType;
 | |
| use style::context::ReflowGoal;
 | |
| 
 | |
| /// Trait for elements with defined activation behavior
 | |
| pub trait Activatable {
 | |
|     fn as_element(&self) -> ∈
 | |
| 
 | |
|     // Is this particular instance of the element activatable?
 | |
|     fn is_instance_activatable(&self) -> bool;
 | |
| 
 | |
|     // https://html.spec.whatwg.org/multipage/#run-pre-click-activation-steps
 | |
|     fn pre_click_activation(&self);
 | |
| 
 | |
|     // https://html.spec.whatwg.org/multipage/#run-canceled-activation-steps
 | |
|     fn canceled_activation(&self);
 | |
| 
 | |
|     // https://html.spec.whatwg.org/multipage/#run-post-click-activation-steps
 | |
|     fn activation_behavior(&self, event: &Event, target: &EventTarget);
 | |
| 
 | |
|     // https://html.spec.whatwg.org/multipage/#implicit-submission
 | |
|     fn implicit_submission(&self, ctrlKey: bool, shiftKey: bool, altKey: bool, metaKey: bool);
 | |
| 
 | |
|     // https://html.spec.whatwg.org/multipage/#concept-selector-active
 | |
|     fn enter_formal_activation_state(&self) {
 | |
|         self.as_element().set_active_state(true);
 | |
| 
 | |
|         let win = window_from_node(self.as_element());
 | |
|         win.reflow(ReflowGoal::ForDisplay,
 | |
|                    ReflowQueryType::NoQuery,
 | |
|                    ReflowReason::ElementStateChanged);
 | |
|     }
 | |
| 
 | |
|     fn exit_formal_activation_state(&self) {
 | |
|         self.as_element().set_active_state(false);
 | |
| 
 | |
|         let win = window_from_node(self.as_element());
 | |
|         win.reflow(ReflowGoal::ForDisplay,
 | |
|                    ReflowQueryType::NoQuery,
 | |
|                    ReflowReason::ElementStateChanged);
 | |
|     }
 | |
| }
 | |
| 
 | |
| /// Whether an activation was initiated via the click() method
 | |
| #[derive(PartialEq)]
 | |
| pub enum ActivationSource {
 | |
|     FromClick,
 | |
|     NotFromClick,
 | |
| }
 | |
| 
 | |
| // https://html.spec.whatwg.org/multipage/#run-synthetic-click-activation-steps
 | |
| pub fn synthetic_click_activation(element: &Element,
 | |
|                                   ctrlKey: bool,
 | |
|                                   shiftKey: bool,
 | |
|                                   altKey: bool,
 | |
|                                   metaKey: bool,
 | |
|                                   source: ActivationSource) {
 | |
|     // Step 1
 | |
|     if element.click_in_progress() {
 | |
|         return;
 | |
|     }
 | |
|     // Step 2
 | |
|     element.set_click_in_progress(true);
 | |
|     // Step 3
 | |
|     let activatable = element.as_maybe_activatable();
 | |
|     if let Some(a) = activatable {
 | |
|         a.pre_click_activation();
 | |
|     }
 | |
| 
 | |
|     // Step 4
 | |
|     // https://html.spec.whatwg.org/multipage/#fire-a-synthetic-mouse-event
 | |
|     let win = window_from_node(element);
 | |
|     let target = element.upcast::<EventTarget>();
 | |
|     let mouse = MouseEvent::new(win.r(),
 | |
|                                 DOMString::from("click"),
 | |
|                                 EventBubbles::DoesNotBubble,
 | |
|                                 EventCancelable::NotCancelable,
 | |
|                                 Some(win.r()),
 | |
|                                 1,
 | |
|                                 0,
 | |
|                                 0,
 | |
|                                 0,
 | |
|                                 0,
 | |
|                                 ctrlKey,
 | |
|                                 shiftKey,
 | |
|                                 altKey,
 | |
|                                 metaKey,
 | |
|                                 0,
 | |
|                                 None);
 | |
|     let event = mouse.upcast::<Event>();
 | |
|     if source == ActivationSource::FromClick {
 | |
|         event.set_trusted(false);
 | |
|     }
 | |
|     target.dispatch_event(event);
 | |
| 
 | |
|     // Step 5
 | |
|     if let Some(a) = activatable {
 | |
|         if event.DefaultPrevented() {
 | |
|             a.canceled_activation();
 | |
|         } else {
 | |
|             // post click activation
 | |
|             a.activation_behavior(event, target);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     // Step 6
 | |
|     element.set_click_in_progress(false);
 | |
| }
 |