@@ -9,8 +9,6 @@ import { privateApis as composePrivateApis } from '@wordpress/compose';
99import { getActiveFormats } from '../../get-active-formats' ;
1010import { isCollapsed } from '../../is-collapsed' ;
1111import { updateFormats } from '../../update-formats' ;
12- import { ownsSelection } from '../../owns-selection' ;
13- import { subscribeOwnedListener } from '../../subscribe-owned-listener' ;
1412import { unlock } from '../../lock-unlock' ;
1513
1614const { subscribeDelegatedListener } = unlock ( composePrivateApis ) ;
@@ -131,13 +129,9 @@ export default ( props ) => ( element ) => {
131129 return ;
132130 }
133131
134- // Ensure the active element is the rich text element, or that the
135- // element owns the selection through a focused editing host (the
136- // editable block editor canvas wrapper).
137- if (
138- ownerDocument . activeElement !== element &&
139- ! ownsSelection ( element )
140- ) {
132+ // Ensure the active element is the rich text element. The listener
133+ // stays subscribed but no-ops for instances that aren't focused.
134+ if ( ownerDocument . activeElement !== element ) {
141135 return ;
142136 }
143137
@@ -195,11 +189,10 @@ export default ( props ) => ( element ) => {
195189 }
196190
197191 function onCompositionStart ( ) {
198- // Do not update the selection when characters are being composed as
199- // this rerenders the component and might destroy internal browser
200- // editing state. `handleSelectionChange` returns early while
201- // composing.
202192 isComposing = true ;
193+ // `handleSelectionChange` returns early while composing, so the
194+ // selection is not updated as characters are composed (which rerenders
195+ // the component and might destroy internal browser editing state).
203196 // Remove the placeholder. Since the rich text value doesn't update
204197 // during composition, the placeholder doesn't get removed. There's no
205198 // need to re-add it, when the value is updated on compositionend it
@@ -235,24 +228,8 @@ export default ( props ) => ( element ) => {
235228 props . current ;
236229
237230 // When the whole editor is editable, let writing flow handle
238- // selection state .
231+ // selection.
239232 if ( element . parentElement . closest ( '[contenteditable="true"]' ) ) {
240- // A nested editable element does not receive a caret from being
241- // focused, unlike an editing host. When the element does not
242- // contain the selection, restore the internal record's selection,
243- // or match the editing host behavior for programmatic focus and
244- // place the caret at the start.
245- const selection = defaultView . getSelection ( ) ;
246- if (
247- ! selection . anchorNode ||
248- ! element . contains ( selection . anchorNode )
249- ) {
250- if ( isSelected && record . current . start !== undefined ) {
251- applyRecord ( record . current ) ;
252- } else {
253- selection . collapse ( element , 0 ) ;
254- }
255- }
256233 return ;
257234 }
258235
@@ -280,63 +257,22 @@ export default ( props ) => ( element ) => {
280257 window . queueMicrotask ( handleSelectionChange ) ;
281258 }
282259
283- /**
284- * The native `selectionchange` event is asynchronous and may not have
285- * been dispatched yet for a preceding selection change when the next
286- * event arrives. Sync the record immediately so the handlers of the very
287- * event being processed read a fresh record.
288- */
289- function ensureSelectionSync ( ) {
290- if ( ownerDocument . activeElement === element ) {
291- return ;
292- }
293- if ( ! ownsSelection ( element ) ) {
294- return ;
295- }
296- handleSelectionChange ( ) ;
297- }
298-
299260 // `input` and `compositionend` must run before block-editor's
300261 // `input-rules.js` element-level listeners, which call `getValue()`
301262 // reading `record.current` updated by our `onInput`. Use capture phase
302263 // so we fire before any ancestor bubble handlers.
303- const unsubscribeInput = subscribeOwnedListener (
264+ const unsubscribeInput = subscribeDelegatedListener (
304265 element ,
305266 'input' ,
306267 onInput ,
307268 true
308269 ) ;
309- // Bound to the document with capture so it runs before any other
310- // listener of the event. Subscribed for every event type whose handlers
311- // read the internal record.
312- const unsubscribeEnsureSelection = [
313- 'keydown' ,
314- 'beforeinput' ,
315- 'copy' ,
316- 'cut' ,
317- 'paste' ,
318- ] . map ( ( eventType ) =>
319- subscribeDelegatedListener (
320- ownerDocument ,
321- eventType ,
322- ensureSelectionSync ,
323- true
324- )
325- ) ;
326- // Permanently subscribed: `handleSelectionChange` checks whether the
327- // element has focus or owns the selection itself. The shared underlying
328- // delegated listener keeps the number of native listeners constant.
329- const unsubscribeSelectionChange = subscribeDelegatedListener (
330- ownerDocument ,
331- 'selectionchange' ,
332- handleSelectionChange
333- ) ;
334- const unsubscribeCompositionStart = subscribeOwnedListener (
270+ const unsubscribeCompositionStart = subscribeDelegatedListener (
335271 element ,
336272 'compositionstart' ,
337273 onCompositionStart
338274 ) ;
339- const unsubscribeCompositionEnd = subscribeOwnedListener (
275+ const unsubscribeCompositionEnd = subscribeDelegatedListener (
340276 element ,
341277 'compositionend' ,
342278 onCompositionEnd ,
@@ -347,13 +283,21 @@ export default ( props ) => ( element ) => {
347283 'focusin' ,
348284 onFocus
349285 ) ;
286+ // Permanently subscribed rather than added on focus and removed on blur:
287+ // `handleSelectionChange` checks whether the element is focused itself,
288+ // and the shared underlying delegated listener keeps the number of native
289+ // listeners constant.
290+ const unsubscribeSelectionChange = subscribeDelegatedListener (
291+ ownerDocument ,
292+ 'selectionchange' ,
293+ handleSelectionChange
294+ ) ;
350295
351296 return ( ) => {
352297 unsubscribeInput ( ) ;
353- unsubscribeEnsureSelection . forEach ( ( unsubscribe ) => unsubscribe ( ) ) ;
354- unsubscribeSelectionChange ( ) ;
355298 unsubscribeCompositionStart ( ) ;
356299 unsubscribeCompositionEnd ( ) ;
357300 unsubscribeFocus ( ) ;
301+ unsubscribeSelectionChange ( ) ;
358302 } ;
359303} ;
0 commit comments