When doing operations on `RopeIndex` that need to make slices of lines,
this change makes it so that the resulting index does not intersect a
character. This is important because Rust will panic if you attempt to
slice a string that way.
Testing: This change adds a WPT crash test and a `Rope` unit test.
Fixes: #42217.
Signed-off-by: Martin Robinson <mrobinson@igalia.com>
This change is a reworking of the shaping code and simplification of the
`GlyphRun` data structure.
The shaper was written between 2012 and 2014 against an early version of
Rust. It was originally written to have a single glyph entry per UTF-8
code point. This is useful when you always need to iterate through
glyphs based on UTF-8 code points. Unfortunately, this required a
tri-level data structure to hold detailed glyphs and meant that CJK
characters took over 3x the memory usage of ASCII characters. In
addition, iterating through glyphs (the most common and basic operation
on shaped text) required doing a lookup involving a binary search for
detailed glyphs (ones that had large advances or mapped to more or less
than a single UTF-8 code point).
The new design of the `GlyphStore` is instead based on `chars` in the
input string. These are tracked during layout so that the resulting
glyph output can be interpreted relatively to its original character
offset in the containing IFC. We are already dealing with IFC text on a
per-character basis for a variety of reasons (such as text
transformation and whitespace collapse). In addition, we will now able
to
implement mapping between the character offsets before and after layout
transformations of the original DOM string.
Now the penalty of more complex glyph iteration is only paid when
transforming glyph offsets to character offsets. Currently this is only
done for selections and clicking in text boxes, both of which are much
less common than layout.
This change does not properly handle selections in RTL text, though
rendering and basic selection and visual movement works (though buggy).
It does not seem like this affects the performance of shaping based on
measurement using the text shaping performance counters. This likely
means that the performance of shaping is dominated on our machines by
HarfBuzz. We noticed no performance degradation in Speedometer when run
on a M3 Mac.
Followup work:
- Properly handle selection in RTL text.
- Support mapping from original DOM character offsets to offsets in
layout after text transformation and whitespace collapse. This is now
possible.
Testing: This causes some tests to pass and a few to fail. This is
likely
due to the fact that we are handling glyphs more consistently while
shaping. Of the new failures:
- `letter-spacing-bengali-yaphala-001.html`,
`letter-spacing-cursive-001.html`, `font-feature-settings-tibetan.html`
where passing before probably because we were not applying letter
spacing to detailed glyphs. These scripts should not have letter spacing
applied to them, because they are cursive -- which we never implemented
properly. It will be handled in a a followup.
- `shaping-arabic-diacritics-001.html`: This was a false pass. The tests
verifies that Arabic diacritics are applied to NBSP. This wasn't
happening before nor after this change, but the results matched anyway.
Now they don't, but before and after are equally broken.
-
Fixes: #216
Part of #35540.
---------
Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Oriol Brufau <obrufau@igalia.com>
This change adds basic support for selecting text via dragging the mouse
in text inputs. This is currently handled entirely inside of text
inputs, but will be integrated into a more global drag handler when that
is implemented in the `DocumentEventHandler`. This means that events
that happen outside of the text input currently don't update the
selection.
Testing: This adds Servo-specific WPT-style tests. There are WPT tests
that test this sort of behavior, but I believe they rely on selection
events, which we have no implemented yet (we will soon).
Signed-off-by: Martin Robinson <mrobinson@igalia.com>
In text inputs, the edit point can be equal to a non-`None` selection
origin for a variety of reasons such as when the selection is set by a
DOM API or when a composition event inserts a zero length string. In
both of these cases, we should treat the selection as collapsed. It is
important to do this because for the purposes of arrow key movement and
context menu entries, we should think of the text input as not having a
selection at all.
Testing: This change adds a Servo-specific WPT test, as key press
behavior is
platform specific.
Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Some platforms send empty composition events (winit on Linux, for
example). These should be ignored appropriately as they can cause
unecessary rendering updates even when the content of the text area does
not change.
Testing: This is mainly an optimization, so no tests are added. It does
prevent
some misbehavior, but that is a secondary effect fixed by #41978.
Signed-off-by: Martin Robinson <mrobinson@igalia.com>
This constructors takes more arguments than it needs to. The three
arguments removed are almost always the default values. The exception is
in the unit tests. In this change those calls are modified to use
`set_max_length` like is done in the actual script code.
Testing: This is just a simplification of the code, so should be covered
by existing tests.
Signed-off-by: Martin Robinson <mrobinson@igalia.com>
We should not move the text cursor or change the text selection when
non-primary (such as the right) mouse buttons are pressed. Doing so
interferes with the common operations:
1. Select text
2. Open context menu
3. Copy text
This change fixes that.
Testing: New WPT-style tests are added.
Signed-off-by: Martin Robinson <mrobinson@igalia.com>
This change adds support for updating the selection when double and
triple clicking in text fields. Double clicking selects the most
relevant word while triple clicking selects the entire line.
Testing: This change adds unit tests for `Rope` as well as a
Servo-specific WPT style test. These behaviors are platform
dependent.
Signed-off-by: Martin Robinson <mrobinson@igalia.com>
This change moves all single versus multiline input handling to script.
It didn't really belong in `Rope`. In addition, it ensures that all
insertions into single line text boxes are sanitized by replacing
linebreaks with spaces.
Testing: As pasting is hard to test with a WPT-style test, this changes
includes new unit tests.
Signed-off-by: Martin Robinson <mrobinson@igalia.com>
`TextInput` internally stores a rope of text content, one segment for
each line. This change separates out the rope into a separate `Rope`
data structure that can be shared with other parts of Servo.
`TextInput` needs to move through text content in a variety of ways,
depending on the keys pressed when interacting with a text area. This
change provides a unified movement API in both `Rope` and in
`TextInput` ensuring that selection is handled consistently (apart from
a few minor quirks [^1]). This simplifies the code an improves
interactive behavior.
[^1]: One of these quirks is that the edit point will move to
directional end of the motion when collapsing a selection, but only when
moving by grapheme or word (and not by line). These quirks existed in an
undocumented way previously and they are preserved with code comments.
Testing: This is covered by existing unit tests (updated for the new
API) and
the WPT suite.
Signed-off-by: Martin Robinson <mrobinson@igalia.com>
DOM APIs for interacting with selection and text in text inputs
`<input type=text>` and `<textarea>` all accept offsets and lengths in
UTF-16 code units. Servo was not converting all of these offsets into
UTF-8 code units. This change makes it so that this conversion is done
more thoroughly and makes it clear when the code is dealing with UTF-8
offsets and UTF-16 offsets.
Helper functions are added for doing this conversion in both directions
as it is necessary. In addition, a `char` iterator is added for
`TextInput` as it is useful for doing this conversion. It will be used
more completely in the future when a `Rope` data structure is extracted
from `TextInput`.
Finally, specification text is added to all of the DOM implementation
touched here.
Testing: This change includes a new WPT crash test as well as a series
of unit
tests to verify conversion between UTF-8 and UTF-16 offsets.
Fixes#36719.
Fixes#20028.
Fixes#39184.
Signed-off-by: Martin Robinson <mrobinson@igalia.com>
This moves the Unicode offset types to the `base` crate and makes them
generally more usable throughout the Servo codebase. In addition, they
are renamed to use Rust naming and to be a bit more consistent:
- `UTF8Bytes` -> `Utf8CodeUnitLength`
- `UTF16CodeUnits` -> `Utf16CodeUnitLength`
There is also a bit more documentation explaining their use.
This is preparation for fixing issues with UTF-16 offsets in editable
text fields.
Testing: This does not change behavior so existing WPT tests should
suffice.
Signed-off-by: Martin Robinson <mrobinson@igalia.com>
This change replaces a couple calls to `Iterator::fold` with
`Iterator::sum`.
Testing: No tests, this is a small cleanup PR.
Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
Use `InputEvent` for text input and set appropriate values for the
`composed`, `data`, `isComposing`, and `inputType` attributes. Use a
placeholder for `dataTransfer` attribute and `getTargetRanges` function,
as they are only applicable to contenteditable, which isn't implemented.
Testing: I added two tests under `tests/wpt/mozilla/tests/input-events`
based on the similarly named ones for contenteditable under
`tests/wpt/tests/input-events`.
Fixes: https://github.com/servo/servo/issues/36398
---------
Signed-off-by: Excitable Snowball <excitablesnowball@gmail.com>
This replaces the implementation of is_empty and len with more efficient
representation without conversion
and allocation based on the underlying bytes.
For this we use a new view, EncodedBytesView.
Additionally, we implement a new macro `match_domstring_ascii!` which
allows simple match clauses
matching ascii strings with DOMStrings without conversion/allocation.
The macro will panic in debug builds if the strings are non-ascii but
will not match all DOMStrings correctly.
We replaced the usage of `DOMString::str()` in many places with this
macro.
Testing: len and is_empty were already covered by tests.
match_domstring_ascii! has more unit tests added with this PR.
---------
Signed-off-by: Narfinger <Narfinger@users.noreply.github.com>
This implements LazyDOMString (from now on DOMString) as outlined in
https://github.com/servo/servo/issues/39479.
Constructing from a *mut JSString we keep the in a
RootedTraceableBox<Heap<*mut JSString>> and transform
the string into a rust string if necessary via the `make_rust_string`
method.
Methods used in script are implemented on this string. Currently we
transform the string at all times.
But in the future more efficient implementations are possible.
We implement the safety critical sections in a separate module
DOMStringInner which allows simple constructors, `make_rust_string` and
the `bytes` method.
This method returns the new type `EncodedBytes` which contains the
reference to the underlying string in either format.
Testing: WPT tests still seem to work, so this should test this
functionality.
---------
Signed-off-by: Narfinger <Narfinger@users.noreply.github.com>
This is part of the future work of implementing LazyDOMString as
outlined in issue #39479.
We use str() method or direct implementations on DOMString for these
methods. We also change some types.
This is independent of https://github.com/servo/servo/pull/39480
Signed-off-by: Narfinger Narfinger@users.noreply.github.com
Testing: This is essentially just renaming a method and a type and
should not change functionality.
Signed-off-by: Narfinger <Narfinger@users.noreply.github.com>
When using the clipboard to paste or modify the contents of an input
element the specification says says[^1] to
> Queue tasks to fire any events that should fire due to the
> modification, see § 5.3 Integration with other scripts and events for
> details.
This change does that, by turning `handle_text_clipboard_action` into
`TextInput::handle_clipboard_event` and having the caller responsible
for executing events. In addition, when content is changed, the node is
dirtied, forcing a relayout.
[^1]: https://www.w3.org/TR/clipboard-apis/#paste-action
Testing: This is difficult to test because we do not have test harness
support for input events currently. There is a manual test for this in
the linked bug which is now passing.
Fixes: #37074.
Signed-off-by: Martin Robinson <mrobinson@igalia.com>
With some adjustment for `NamedKey`. The two crates need to be bumped
together to avoid duplicate of `keyboard-types` action.
---------
Signed-off-by: PotatoCP <Kenzie.Raditya.Tirtarahardja@huawei.com>
1. Let `input` JS event be dispatched by `keydown` instead of
`keypress`, according to spec
2. Fire `input` event for Backspace and Delete. But do so only when
something is actually deleted
Testing: Manually tested and compared with other browsers.
Fixes: #37051
cc @xiaochengh
Signed-off-by: Euclid Ye <yezhizhenjiakang@gmail.com>
* test(textinput): Add test for backspace at beginning of line in textarea
Introduce a test to reproduce and verify the fix for backspacing at the
beginning of a line in a multiline textarea. This ensures that pressing
Backspace when the cursor is at the start of a line correctly removes the
newline without deleting the entire previous line’s content.
Related to: #27523
Signed-off-by: Emmanuel Elom <elomemmanuel007@gmail.com>
* fix(textinput): Preserve selection origin when adjusting vertical position
Fixes an issue where pressing Backspace at the beginning of a line in a
textarea incorrectly deleted the entire previous line's content. This happened
because `self.adjust_vertical(-1, select)` modified `selection_origin` and
`edit_point`, but `selection_origin` was not restored before performing the
horizontal adjustment. As a result, `self.selection_start()` and
`self.selection_end()` were inconsistent, leading to `replace_operation`
erasing the entire line.
Now, we temporarily store `selection_origin` before adjusting vertical
position and restore it afterward to ensure proper cursor and selection
behavior.
Fixes: #27523
Signed-off-by: Emmanuel Elom <elomemmanuel007@gmail.com>
---------
Signed-off-by: Emmanuel Elom <elomemmanuel007@gmail.com>
Co-authored-by: Josh Matthews <josh@joshmatthews.net>
* Update to rust 1.85
This is needed for cargo-deny
Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
* Upgrade crown
Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
* Clippy fixes
Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
* Re-upgrade cargo-deny to 0.18
Keeping it locked to 0.18 just in case they
update their required rustc version again
Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
---------
Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
Add a `ClipboardDelegate` to the `WebView` API and a default
implementation in libservo for this delegate that works on Mac, Windows,
and Linux. Support for Android will be added in the future. This means
that embedders do not need to do anything special to get clipboard
support, but can choose to override it or implement it for other
platforms.
In addition, this adds support for handling fetches of clipboard contents
and renames things to reflect that eventually other types of clipboard
content will be supported. Part of this is removing the string
argument from the `ClipboardEventType::Paste` enum because script will
need to get other types of content from the clipboard than just a
string. It now talks to the embedder to get this information directly.
Signed-off-by: Martin Robinson <mrobinson@igalia.com>
`EmbedderMsg` was previously paired with an implicit
`Option<WebViewId>`, even though almost all variants were either always
`Some` or always `None`, depending on whether there was a `WebView
involved.
This patch adds the `WebViewId` to as many `EmbedderMsg` variants as
possible, so we can call their associated `WebView` delegate methods
without needing to check and unwrap the `Option`. In many cases, this
required more changes to plumb through the `WebViewId`.
Notably, all `Request`s now explicitly need a `WebView` or not, in order
to ensure that it is passed when appropriate.
Signed-off-by: Delan Azabani <dazabani@igalia.com>
Co-authored-by: Martin Robinson <mrobinson@igalia.com>
These operations are deprecated and might be removed
in a future rust version. Clippy is also complaining
about them.
Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
Forward WebDriver CompositionEvent
Dispatch composition events in JS.
Insert characters from composition events to text input.
CompositionEvents currently can only be
created by WebDriver and not by embedders.
<!-- Please describe your changes on the following line: -->
---
<!-- 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
- [ ] These changes fix #__ (github issue number if applicable).
<!-- Either: -->
- [ ] 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/22224)
<!-- Reviewable:end -->
Dispatch composition events in JS.
Insert characters from composition events to text input.
CompositionEvents currently can only be
created by WebDriver and not by embedders.