Skip to content
This repository has been archived by the owner on Feb 6, 2023. It is now read-only.

Commit

Permalink
Type selection object in editOnInput.
Browse files Browse the repository at this point in the history
Summary:
Recently I merged D20582792— it was a quick fix to a a huge problem that was causing thousands of exceptions for our users.

It could've been prevented with better typing, but wasn't caught by flow because the type of the selection object was assumed to be null.

Here I implement the type of the selection object, and in the process catch other elements that could be null and add safeguards to make our code more reliable. This are places where the previous implementation would've thrown an exception.

Reviewed By: kedromelon

Differential Revision: D20636236

fbshipit-source-id: 6e27040120256e3885bfdd2d9cbedf5e57988062
  • Loading branch information
mrkev authored and facebook-github-bot committed Apr 13, 2020
1 parent 97dd19b commit 41beae1
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 3 deletions.
11 changes: 8 additions & 3 deletions src/component/handlers/edit/editOnInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

'use strict';

import type {SelectionObject} from 'DraftDOMTypes';
import type DraftEditor from 'DraftEditor.react';

const DraftModifier = require('DraftModifier');
Expand Down Expand Up @@ -65,14 +66,14 @@ function editOnInput(editor: DraftEditor, e: SyntheticInputEvent<>): void {
}
// at this point editor is not null for sure (after input)
const castedEditorElement: HTMLElement = (editor.editor: any);
const domSelection = castedEditorElement.ownerDocument.defaultView.getSelection();
const domSelection: SelectionObject = castedEditorElement.ownerDocument.defaultView.getSelection();

const {anchorNode, isCollapsed} = domSelection;
const isNotTextOrElementNode =
anchorNode?.nodeType !== Node.TEXT_NODE &&
anchorNode?.nodeType !== Node.ELEMENT_NODE;

if (isNotTextOrElementNode) {
if (anchorNode == null || isNotTextOrElementNode) {
// TODO: (t16149272) figure out context for this change
return;
}
Expand All @@ -86,10 +87,14 @@ function editOnInput(editor: DraftEditor, e: SyntheticInputEvent<>): void {
// https://chromium.googlesource.com/chromium/src/+/a3b600981286b135632371477f902214c55a1724
// To work around, we'll merge the sibling text nodes back into this one.
const span = anchorNode.parentNode;
if (span == null) {
// Handle null-parent case.
return;
}
anchorNode.nodeValue = span.textContent;
for (
let child = span.firstChild;
child !== null;
child != null;
child = child.nextSibling
) {
if (child !== anchorNode) {
Expand Down
28 changes: 28 additions & 0 deletions src/component/utils/DraftDOMTypes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/**
* (c) Facebook, Inc. and its affiliates. Confidential and proprietary.
*
* Types for things in the DOM used in Draft.js. These should eventaully be
* added to the flow DOM lib itself.
*
* @emails oncall+draft_js
* @flow strict
* @format
*/

'use strict';

// https://developer.mozilla.org/en-US/docs/Web/API/Selection
export type SelectionObject = {|
/**
* Returns the Node in which the selection begins. Can return null if
* selection never existed in the document (e.g., an iframe that was
* never clicked on). */
anchorNode: ?Node,
anchorOffset: number,
focusNode: ?Node,
focusOffset: number,
isCollapsed: boolean,
rangeCount: number,
type: string,
// ...etc. This is a non-exhaustive definition.
|};

0 comments on commit 41beae1

Please sign in to comment.