Page MenuHomePhabricator

plainText sanitization discards the text of nodes that keep it in attributes
Open, Needs TriagePublic

Description

Summary

ve.dm.LinearData#sanitize( { plainText: true } ) removes every element that is not a paragraph, but a node's text is often held in its attributes rather than in the linear data, so the text is deleted with the node. The patches on T431638 avoid this for ordinary pastes by preferring the clipboard's own text/plain, which leaves the lossy primitive in place on the two paths that cannot use it: paste-special, and any paste or drop that carries no text/plain.

Technical notes

Parsoid wraps every literal {, | and } in <span typeof="mw:Entity">, so on those two paths the characters are deleted with the nodes. Measured on the current code: paste-special of {{subst:Foo|1=bar}} and a&nbsp;nbsp inserts subst:Foo1=bar and anbsp, and a source-mode paste whose clipboard holds only text/html turns the {{tlxs}} example from Wikipedia:Barnstars into subst:The Random Acts of Kindness Barnstar1=message ~~~~2=alt. A copied <pre> or <syntaxhighlight> block sanitizes to nothing at all, because its source lives in mw.body.extsrc.

ve.dm.Node.static.getText already exists for this (added for T418474) and ve.dm.MWEntityNode implements it, but sanitize() deletes the node before anything consults it. Substituting a node's own plain text fixes all three cases: a content node becomes characters, a block node is wrapped in a paragraph per line, since characters cannot live outside a content branch node. This is a no-op for node types that do not implement getText, so extension nodes that render their own source opt in through a new usesSourceAsPlainText static reading mw.body.extsrc<pre> in ve-mw, <syntaxhighlight> in SyntaxHighlight_GeSHi. Nodes whose rendering is not their source, such as transclusions and references, stay excluded and keep relying on the clipboard text or on conversion.

Plain text can only ever summarise these nodes: it gives the source of a <syntaxhighlight> without its tag. Today that loss is silent, because wasPlain calls isPlainText() with ignoreNonContentNodes, which skips block nodes, so a paste that is entirely a <pre> looks plain and the "Convert wikitext?" context item is never offered. Until now the emptiness itself triggered an immediate conversion instead; once the extraction stops being empty, that rescue no longer fires, so wasPlain has to account for nodes whose content is not in the linear data.

Acceptance criteria

  • Paste-special of Parsoid-rendered template markup keeps its braces, pipes and non-breaking spaces, in both visual and source mode.
  • A source-mode paste or drop whose clipboard carries only text/html inserts the same text as one that also carries text/plain.
  • A copied <pre> or <syntaxhighlight> block inserts its source text instead of nothing, and still offers to convert the paste to wikitext so the tag can be recovered.

Event Timeline

Change #1321636 had a related patch set uploaded (by DLynch; author: DLynch):

[VisualEditor/VisualEditor@master] LinearData: Keep node text when sanitizing to plain text

https://gerrit.wikimedia.org/r/1321636

Change #1321637 had a related patch set uploaded (by DLynch; author: DLynch):

[mediawiki/extensions/VisualEditor@master] MWExtensionNode: use an extension node's source as its plain text value

https://gerrit.wikimedia.org/r/1321637

Change #1321638 had a related patch set uploaded (by DLynch; author: DLynch):

[mediawiki/extensions/SyntaxHighlight_GeSHi@master] VE: use the highlighted source as the node's plain text value

https://gerrit.wikimedia.org/r/1321638