Skip to content
Permalink
Browse files

Auto merge of #25674 - pshaughn:selection, r=jdm

Selection DOM interface (but not actual UI selections)

This is work towards #7492.

I put new tests in the mozilla-specific directory rather than the main tree because I'm not sure how upstreamable they are; I'd like opinions on that point.

This adds a new exposed interface, which I understand from tests/mozilla/interfaces.html is something requiring specific approval from someone allowed to give that approval.

Things that aren't done here:
- The spec doesn't describe what selection/script-and-style-elements.html wants, and what it wants seems to require some sort of layout query.
- Actual UI interactions are not present at all; selection.rs has a few TODOs saying which methods I believe the eventual UI code should call for what actions, but there are a lot of fine points here that I don't know about.
- I haven't touched Node; you can ask if a node's in the Selection's Range the same way you'd ask about any other Range, but there's not a faster path just for selection layout.
  • Loading branch information
bors-servo committed Feb 14, 2020
2 parents 38adec6 + 5ef3358 commit 4f36472b6fed75568c651cbbeecc6678791018a9
Showing with 812 additions and 8,468 deletions.
  1. +1 −0 components/atoms/static_atoms.txt
  2. +18 −0 components/script/dom/document.rs
  3. +2 −0 components/script/dom/macros.rs
  4. +1 −0 components/script/dom/mod.rs
  5. +54 −0 components/script/dom/range.rs
  6. +515 −0 components/script/dom/selection.rs
  7. +6 −0 components/script/dom/webidls/Document.webidl
  8. +6 −0 components/script/dom/webidls/EventHandler.webidl
  9. +32 −0 components/script/dom/webidls/Selection.webidl
  10. +6 −0 components/script/dom/webidls/Window.webidl
  11. +6 −0 components/script/dom/window.rs
  12. +2 −0 tests/wpt/include.ini
  13. +0 −5 tests/wpt/metadata/custom-elements/reactions/Selection.html.ini
  14. +0 −107 tests/wpt/metadata/dom/ranges/Range-mutations-appendChild.html.ini
  15. +0 −578 tests/wpt/metadata/dom/ranges/Range-mutations-appendData.html.ini
  16. +0 −4,214 tests/wpt/metadata/dom/ranges/Range-mutations-dataChange.html.ini
  17. +0 −848 tests/wpt/metadata/dom/ranges/Range-mutations-deleteData.html.ini
  18. +0 −116 tests/wpt/metadata/dom/ranges/Range-mutations-insertBefore.html.ini
  19. +0 −575 tests/wpt/metadata/dom/ranges/Range-mutations-insertData.html.ini
  20. +0 −32 tests/wpt/metadata/dom/ranges/Range-mutations-removeChild.html.ini
  21. +0 −92 tests/wpt/metadata/dom/ranges/Range-mutations-replaceChild.html.ini
  22. +0 −1,721 tests/wpt/metadata/dom/ranges/Range-mutations-replaceData.html.ini
  23. +0 −176 tests/wpt/metadata/dom/ranges/Range-mutations-splitText.html.ini
  24. +0 −3 tests/wpt/metadata/html/browsers/the-window-object/window-properties.https.html.ini
  25. +5 −0 tests/wpt/metadata/selection/script-and-style-elements.html.ini
  26. +31 −1 tests/wpt/mozilla/meta/MANIFEST.json
  27. +1 −0 tests/wpt/mozilla/tests/mozilla/interfaces.html
  28. +25 −0 tests/wpt/mozilla/tests/mozilla/selectionchange/selectionchange_noop.html
  29. +50 −0 tests/wpt/mozilla/tests/mozilla/selectionchange/selectionchange_range.html
  30. +51 −0 tests/wpt/mozilla/tests/mozilla/selectionchange/selectionchange_selection.html
@@ -109,6 +109,7 @@ seeked
seeking
select
selectend
selectionchange
selectstart
serif
signalingstatechange
@@ -79,6 +79,7 @@ use crate::dom::pagetransitionevent::PageTransitionEvent;
use crate::dom::processinginstruction::ProcessingInstruction;
use crate::dom::promise::Promise;
use crate::dom::range::Range;
use crate::dom::selection::Selection;
use crate::dom::servoparser::ServoParser;
use crate::dom::shadowroot::ShadowRoot;
use crate::dom::storageevent::StorageEvent;
@@ -400,6 +401,8 @@ pub struct Document {
/// https://html.spec.whatwg.org/multipage/#concept-document-csp-list
#[ignore_malloc_size_of = "Defined in rust-content-security-policy"]
csp_list: DomRefCell<Option<CspList>>,
/// https://w3c.github.io/slection-api/#dfn-selection
selection: MutNullableDom<Selection>,
}

#[derive(JSTraceable, MallocSizeOf)]
@@ -2909,6 +2912,7 @@ impl Document {
media_controls: DomRefCell::new(HashMap::new()),
dirty_webgl_contexts: DomRefCell::new(HashMap::new()),
csp_list: DomRefCell::new(None),
selection: MutNullableDom::new(None),
}
}

@@ -4618,6 +4622,11 @@ impl DocumentMethods for Document {
// TODO: https://github.com/servo/servo/issues/21936
Node::replace_all(None, self.upcast::<Node>());

// Specs and tests are in a state of flux about whether
// we want to clear the selection when we remove the contents;
// WPT selection/Document-open.html wants us to not clear it
// as of Feb 1 2020

// Step 12
if self.is_fully_active() {
let mut new_url = entry_responsible_document.url();
@@ -4790,6 +4799,15 @@ impl DocumentMethods for Document {
None => Err(Error::InvalidAccess),
}
}

// https://w3c.github.io/selection-api/#dom-document-getselection
fn GetSelection(&self) -> Option<DomRoot<Selection>> {
if self.has_browsing_context {
Some(self.selection.or_init(|| Selection::new(self)))
} else {
None
}
}
}

fn update_with_current_time_ms(marker: &Cell<u64>) {
@@ -488,6 +488,8 @@ macro_rules! global_event_handlers(
event_handler!(seeked, GetOnseeked, SetOnseeked);
event_handler!(seeking, GetOnseeking, SetOnseeking);
event_handler!(select, GetOnselect, SetOnselect);
event_handler!(selectionchange, GetOnselectionchange, SetOnselectionchange);
event_handler!(selectstart, GetOnselectstart, SetOnselectstart);
event_handler!(show, GetOnshow, SetOnshow);
event_handler!(stalled, GetOnstalled, SetOnstalled);
event_handler!(submit, GetOnsubmit, SetOnsubmit);
@@ -477,6 +477,7 @@ pub mod rtcpeerconnectioniceevent;
pub mod rtcsessiondescription;
pub mod rtctrackevent;
pub mod screen;
pub mod selection;
pub mod serviceworker;
pub mod serviceworkercontainer;
pub mod serviceworkerglobalscope;
@@ -2,6 +2,7 @@
* 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::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::CharacterDataBinding::CharacterDataMethods;
use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeConstants;
use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
@@ -24,6 +25,7 @@ use crate::dom::documentfragment::DocumentFragment;
use crate::dom::element::Element;
use crate::dom::htmlscriptelement::HTMLScriptElement;
use crate::dom::node::{Node, ShadowIncluding, UnbindContext};
use crate::dom::selection::Selection;
use crate::dom::text::Text;
use crate::dom::window::Window;
use dom_struct::dom_struct;
@@ -37,6 +39,16 @@ pub struct Range {
reflector_: Reflector,
start: BoundaryPoint,
end: BoundaryPoint,
// A range that belongs to a Selection needs to know about it
// so selectionchange can fire when the range changes.
// A range shouldn't belong to more than one Selection at a time,
// but from the spec as of Feb 1 2020 I can't rule out a corner case like:
// * Select a range R in document A, from node X to Y
// * Insert everything from X to Y into document B
// * Set B's selection's range to R
// which leaves R technically, and observably, associated with A even though
// it will fail the same-root-node check on many of A's selection's methods.
associated_selections: DomRefCell<Vec<Dom<Selection>>>,
}

impl Range {
@@ -50,6 +62,7 @@ impl Range {
reflector_: Reflector::new(),
start: BoundaryPoint::new(start_container, start_offset),
end: BoundaryPoint::new(end_container, end_offset),
associated_selections: DomRefCell::new(vec![]),
}
}

@@ -163,6 +176,9 @@ impl Range {

// https://dom.spec.whatwg.org/#concept-range-bp-set
fn set_start(&self, node: &Node, offset: u32) {
if &self.start.node != node || self.start.offset.get() != offset {
self.report_change();
}
if &self.start.node != node {
if self.start.node == self.end.node {
node.ranges().push(WeakRef::new(&self));
@@ -178,6 +194,9 @@ impl Range {

// https://dom.spec.whatwg.org/#concept-range-bp-set
fn set_end(&self, node: &Node, offset: u32) {
if &self.end.node != node || self.end.offset.get() != offset {
self.report_change();
}
if &self.end.node != node {
if self.end.node == self.start.node {
node.ranges().push(WeakRef::new(&self));
@@ -228,6 +247,26 @@ impl Range {
// Step 6.
Ok(Ordering::Equal)
}

pub fn associate_selection(&self, selection: &Selection) {
let mut selections = self.associated_selections.borrow_mut();
if !selections.iter().any(|s| &**s == selection) {
selections.push(Dom::from_ref(selection));
}
}

pub fn disassociate_selection(&self, selection: &Selection) {
self.associated_selections
.borrow_mut()
.retain(|s| &**s != selection);
}

fn report_change(&self) {
self.associated_selections
.borrow()
.iter()
.for_each(|s| s.queue_selectionchange_task());
}
}

impl RangeMethods for Range {
@@ -821,6 +860,9 @@ impl RangeMethods for Range {
// Step 3.
if start_node == end_node {
if let Some(text) = start_node.downcast::<CharacterData>() {
if end_offset > start_offset {
self.report_change();
}
return text.ReplaceData(start_offset, end_offset - start_offset, DOMString::new());
}
}
@@ -1142,9 +1184,11 @@ impl WeakRangeVec {
entry.remove();
}
if &range.start.node == child {
range.report_change();
range.start.set(context.parent, offset);
}
if &range.end.node == child {
range.report_change();
range.end.set(context.parent, offset);
}
});
@@ -1169,9 +1213,11 @@ impl WeakRangeVec {
entry.remove();
}
if &range.start.node == node {
range.report_change();
range.start.set(sibling, range.StartOffset() + length);
}
if &range.end.node == node {
range.report_change();
range.end.set(sibling, range.EndOffset() + length);
}
});
@@ -1212,9 +1258,11 @@ impl WeakRangeVec {
}

if move_start {
range.report_change();
range.start.set(child, new_offset);
}
if move_end {
range.report_change();
range.end.set(child, new_offset);
}
});
@@ -1273,9 +1321,11 @@ impl WeakRangeVec {
}

if move_start {
range.report_change();
range.start.set(sibling, start_offset - offset);
}
if move_end {
range.report_change();
range.end.set(sibling, end_offset - offset);
}
});
@@ -1289,9 +1339,11 @@ impl WeakRangeVec {
(*self.cell.get()).update(|entry| {
let range = entry.root().unwrap();
if &range.start.node == node && offset == range.StartOffset() {
range.report_change();
range.start.set_offset(offset + 1);
}
if &range.end.node == node && offset == range.EndOffset() {
range.report_change();
range.end.set_offset(offset + 1);
}
});
@@ -1304,10 +1356,12 @@ impl WeakRangeVec {
let range = entry.root().unwrap();
let start_offset = range.StartOffset();
if &range.start.node == node && start_offset > offset {
range.report_change();
range.start.set_offset(f(start_offset));
}
let end_offset = range.EndOffset();
if &range.end.node == node && end_offset > offset {
range.report_change();
range.end.set_offset(f(end_offset));
}
});

0 comments on commit 4f36472

Please sign in to comment.
You can’t perform that action at this time.