`aHTMLEditor` may be `nullptr` and there may have been an `HTMLEditor` before
it's called. Therefore, it may need to adjust IME state even if `aHTMLEditor`
is `nullptr`. So, it should adjust IME state from `aDocument` which should be
same as `aHTMLEditor->GetDocument()` if `aHTMLEditor` is not `nullptr`.
Note that if `IMEStateManager` thinks another `nsPresContext` has focus, it does
not touch IME state. Therefore, it's safe to skip checking the focus state of
the `Document`.
Differential Revision: https://phabricator.services.mozilla.com/D220772
When `HTMLEditor` handles the design mode, `IMEContentObserver` observes the
`<body>` if there is. However, web apps may remove it. Then, we need to
recreate `IMEContentObserver` with emulating a focus move because it's difficult
to compute the difference between the old root and the new root (IME focus
notification sends all content to the parent, it's faster in most cases in this
situation). This was fixed in bug 1911010. However, there are remaining issues
after that.
When new `<body>` or the original `<body>` is connected again,
`IMEContentObserver` does not restart to observe the new `<body>`. So,
`IMEContentObserver` working differently after the `<body>` is even temporarily
removed. This make it harder to reproduce reported bugs.
Additionally, if the document element is removed, `IMEContentObserver` won't
be recreated until the document gets focus again even after new root and/or
`<body>` element is connected. This makes IME users inconvenient with such
tricky editors.
This patch makes `HTMLEditor::NotifyRootChanged` notifies `IMEStateManager`
of the editor root element change. Then, the new method of `IMEStateManager`
updates IME enabled state and recreate `IMEContentObserver` with emulating
a focus move.
Differential Revision: https://phabricator.services.mozilla.com/D220362
`aHTMLEditor` may be `nullptr` and there may have been an `HTMLEditor` before
it's called. Therefore, it may need to adjust IME state even if `aHTMLEditor`
is `nullptr`. So, it should adjust IME state from `aDocument` which should be
same as `aHTMLEditor->GetDocument()` if `aHTMLEditor` is not `nullptr`.
Note that if `IMEStateManager` thinks another `nsPresContext` has focus, it does
not touch IME state. Therefore, it's safe to skip checking the focus state of
the `Document`.
Differential Revision: https://phabricator.services.mozilla.com/D220772
`mozAutoDocUpdate` does not make it in a document change when container node
of `insertBefore` has already been removed from the tree. Therefore, once
`IMEContentObserver::mRootElement` is removed from the DOM tree without a
focus move, `IMEContentObserver` is notified mutations not in a document change.
Similar situations are handled in `IMEStateManager::OnRemoveContent` with
emulating a focus change and that will destroy the active `IMEContentObserver`.
Therefore, if `IMEContentObserver::mRootElement` is removed, we should emulate
a focus move when `IMEStateManager` does not have focused element but there
is active `IMEContentObserver` (that means it is/was in the design mode).
However, checking whether the removed node contains the observing node of the
active `IMEContentObserver` may be expensive. So, doing expensive things in
`IMEStateManager::OnRemoveContent` may make mutations slower. Therefore, this
patch makes `IMEContentObserver` observe `ParentChainChanged` and it let
`IMEStateManager` know that with calling its
`OnParentChainChangedOfObservingElement`. Finally, it calls
`IMEStateManager::OnRemoveContent` to emulate "blur" (and refocus if it's
required).
Differential Revision: https://phabricator.services.mozilla.com/D218696
`mozAutoDocUpdate` does not make it in a document change when container node
of `insertBefore` has already been removed from the tree. Therefore, once
`IMEContentObserver::mRootElement` is removed from the DOM tree without a
focus move, `IMEContentObserver` is notified mutations not in a document change.
Similar situations are handled in `IMEStateManager::OnRemoveContent` with
emulating a focus change and that will destroy the active `IMEContentObserver`.
Therefore, if `IMEContentObserver::mRootElement` is removed, we should emulate
a focus move when `IMEStateManager` does not have focused element but there
is active `IMEContentObserver` (that means it is/was in the design mode).
However, checking whether the removed node contains the observing node of the
active `IMEContentObserver` may be expensive. So, doing expensive things in
`IMEStateManager::OnRemoveContent` may make mutations slower. Therefore, this
patch makes `IMEContentObserver` observe `ParentChainChanged` and it let
`IMEStateManager` know that with calling its
`OnParentChainChangedOfObservingElement`. Finally, it calls
`IMEStateManager::OnRemoveContent` to emulate "blur" (and refocus if it's
required).
Differential Revision: https://phabricator.services.mozilla.com/D218696
When `HTMLEditor::InsertLinkAroundSelectionAsAction()` calls
`HTMLEditor::SetInlinePropertiesAsSubAction()`, it adds 2 elements to the array.
One is for `_moz_dirty` attribute and the other is for `href` attribute because
`InsertTagCommand::DoCommandParam()` uses
`HTMLEditor::CreateElementWithDefaults()`.
Then, `HTMLEditor::SetInlinePropertiesAsSubAction()` wraps the `<img>` into
`<a _moz_dirty="">` with calling `AutoInlineStyleSetter::ApplyStyle()`. Then,
it calls `OnHandle()` with the `<img>` and it collapse the applied range into
the `<img>`. Therefore, when `SetInlinePropertiesAsSubAction()` handles `href`,
there is only collapsed range and it just puts the style into the cache for
applying the style when the user types new text.
So, first, `OnHandle()` should not set the boundary points of the applied range
into non-container node. And also `InsertLinkAroundSelectionAsAction()` should
ignore `_moz_dirty` attribute because if it's truly required,
`AutoInlineStyleSetter` should set the attribute to every new element.
Note that this causes new failure in `editing/run/fontname.html?1001-200`,
but it was accidentally passed. The case is, `queryCommandValue("fontname")`
result after calling `execCommand("fontname", false, "sans-serif")` for
`foo<tt>{<br></tt>}bar`. The result of the DOM tree is
`foo<tt><font face="sans-serif"><br></font></tt>bar` and `Selection` was
collapsed in the `<br>` before, but with this patch, the `<br>` is selected.
Therefore, the result is changed but similar cases in the tests fail too. So I
believe that it accidentally passed with odd `Selection`.
Differential Revision: https://phabricator.services.mozilla.com/D219127
The method may be called without focus. Therefore, it shouldn't refer
`mIsInDesignMode` and it should refer focused element in the window (including
shadows) if there is no selection ranges.
Differential Revision: https://phabricator.services.mozilla.com/D218726
When using autocorrect, we should use `insertReplacementText` according
to https://github.com/w3c/input-events/issues/152. So I would like to
add eContentCommandReplaceText command for this.
Also, this command has an option that is source string text. When
processing text subsitution, parent process doesn't know whether
target replaced text is modified. So I add this option for check.
Differential Revision: https://phabricator.services.mozilla.com/D213511
Now, we have `nsFocusManager::GetFocusedElementStatic()` which returns focused
element if the `nsFocusManager` instance is available. Therefore, if
`nsFocusManager::GetFocusedElement()` users do not use other methods of
`nsFocusManager`, they can use `nsFocusManager::GetFocusedElementStatic()` and
make themselves simpler.
Note that some callers return early if `nsFocusManager` is not available, but
they do not return error and `nsFocusManager` instance is available in most
time of the life time of the process. Therefore, we can simply stop using the
early return.
Differential Revision: https://phabricator.services.mozilla.com/D217527
This is a simple mistake, it does not reset `mIsInDesignMode` when it emulates
a "blur".
Additionally, I realized that it does not check whether the focused element is
in the document or not and emulates "focus" of itself it the focused element is
in a different document. In such case, the editor shouldn't have focus, so,
this is a hidden bug, but "blur" notification may occur asynchronously.
Therefore, this could be a bug only in some edge cases.
Differential Revision: https://phabricator.services.mozilla.com/D216907
Since this issue is too obvious, I am not even sure I am doing the right
thing. This looks like it should be discovered years ago....
As far as I can tell from the spec, dragstart and dragend use the same node
as the target, but it's not always the same node in Firefox.
Differential Revision: https://phabricator.services.mozilla.com/D215827
`HTMLEditor` currently checks whether it's in the `designMode` or not with
checking the `Document` node is in the `designMode`. I.e., it checks the
real-time state of the `Document` node. However, if an instance was in the
`designMode`, it needs to finalize `Selection` even after the `Document` node
becomes not in the `designMode`. Additionally, elements in a shadow DOM in the
`designMode` can have `contenteditable` to make it editable. Then, the editable
elements should works as in the `contenteditable` mode rather than in the
`designMode`. Therefore, current `HTMLEditor::IsInDesignMode()` returns
wrong state when the finalization is called asynchronously or when an editing
host in a shadow DOM whose composed document is in the `designMode`.
This patch fixes known cases of these issues with improving the finalization
(and re-initialization with the new editing mode) and the caller side in
`Document`.
Differential Revision: https://phabricator.services.mozilla.com/D216022
`HTMLEditor` currently checks whether it's in the `designMode` or not with
checking the `Document` node is in the `designMode`. I.e., it checks the
real-time state of the `Document` node. However, if an instance was in the
`designMode`, it needs to finalize `Selection` even after the `Document` node
becomes not in the `designMode`. Additionally, elements in a shadow DOM in the
`designMode` can have `contenteditable` to make it editable. Then, the editable
elements should works as in the `contenteditable` mode rather than in the
`designMode`. Therefore, current `HTMLEditor::IsInDesignMode()` returns
wrong state when the finalization is called asynchronously or when an editing
host in a shadow DOM whose composed document is in the `designMode`.
This patch fixes known cases of these issues with improving the finalization
(and re-initialization with the new editing mode) and the caller side in
`Document`.
Differential Revision: https://phabricator.services.mozilla.com/D216022
MaybeEditorDeletedSourceNode was defined on nsIDragService but is more appropriately defined on nsIDragSession. This was not an issue previously since those were the same object.
Differential Revision: https://phabricator.services.mozilla.com/D211074
Since we now add the widget to the event in
dispatchDOMEventViaPresShellForTesting, WidgetGUIEvents that are sent in
mochitests via that method need to transform their screen coordinates by
nsIWidget::WidgetToScreenOffset, to mirror the transformation by
BrowserParent::TransformParentToChild that they don't get because they
skip the parent process.
This exposes a bunch of things that were done to work around this bug. They
are cleaned up here.
Differential Revision: https://phabricator.services.mozilla.com/D211068
Updates each client of the nsContentUtils method to get the right drag session -- the one for the widget that is currently the source or target of the drag session.
The change to nsDOMWindowUtils::DispatchDOMEventViaPresShellForTesting() supports the change to WidgetDragEvent::InitDropEffectForTests() and enabled a
large number of test fixes in the next patch.
Differential Revision: https://phabricator.services.mozilla.com/D211067
This patch enables the assertions when running:
* The `nsITextInputProcessor` tests
* The IME composition tests under `widget/`
* Under `editor/libeditor/tests`
* Under `editor/libeditor/crashtests`
* Under testing/web-platform/tests/editing`
* Under testing/web-platform/tests/input-events`
Differential Revision: https://phabricator.services.mozilla.com/D211835
This patch enables the assertions when running:
* The `nsITextInputProcessor` tests
* The IME composition tests under `widget/`
* Under `editor/libeditor/tests`
* Under `editor/libeditor/crashtests`
* Under testing/web-platform/tests/editing`
* Under testing/web-platform/tests/input-events`
Differential Revision: https://phabricator.services.mozilla.com/D211835
`input` event listeners may be async functions and may set the text control
element value after the editor ends dispatching an `input` event. Even in this
case, the `input` event listener should not be called recursively by committing
composition which is caused by setting the value because the value is
overwritten by the setting value which was intended by the web app.
Unfortunately, we cannot check whether the value setter is an async `input`
event listener. Therefore, we need to suppress `input` events even after the
editor ends dispatching `input` event.
On the other hand, we should not suppress `input` event if the value is set
by a `compositionupdate` event listener which runs before the editor starts
handling the latest composition change because once we do that, the app won't
receive `input` event for the composition. Therefore, we should not suppress
the editor starts handling the composition change (including during
`beforeinput` event dispatching, but the `beforeinput` is not cancelable, so,
the result will be odd, therefore, we have no tests for the cases).
Therefore, this patch adds a new method to `TextComposition` to make
`TextControlState::SetValue` can check whether the editor is handling the
latest composition change or after that. So, during composition, setting
value should cause `input` events between `TextComposition` starts dispatching a
`compositionupdate` event and `EditorBase` starts handling `eCompositionChange`
event which is dispatched after `compositionupdate`.
Differential Revision: https://phabricator.services.mozilla.com/D214676
As far as testing on Chrome, they don't dispatch any events during setting
text control value (including `compositionupdate` and `compositionend`).
However, we dispatch `compositionupdate`, `compositionend`, `beforeinput` and
`input` synchronously. Therefore, `input` event listener may run again with
the old value. This may make web apps confused because they may not expect
the nested `input` event listener call which is not caused by user intention
nor a `Document.execCommand` call. Therefore, we should stop dispatching
the nested `beforeinput` and `input` events which are caused by committing
composition for setting value since the value will be updated soon, so,
the input caused by committing composition does not occur actually. However,
`compositionend` event should be fired even in the nested event loop because
the web apps may manage whether they has a composition with a pair of
`compositionstart` and `compositionend` event listeners even though that won't
work on Chrome.
Additionally, the nested `compositionupdate` and `compositionend` event may
cause a `Document.execCommand` call. However, that must be unexpected behavior
for web apps and anyway the value will be overwritten to the new value.
Therefore, let's make `Document::ExecCommand` do nothing in the situation.
Differential Revision: https://phabricator.services.mozilla.com/D213756
While handling an "insertParagraph" command, it's closed at least in the editing
host. Therefore, it's file to use editing host outside `<body>` because it
won't make the tree messy outside the editing host.
Differential Revision: https://phabricator.services.mozilla.com/D213376
nsAutoTArray was renamed to AutoTArray.
nsDataHashtable and nsJSThingHashtable don't exist any more.
nsTHashMap and nsTHashSet now exist.
nsDeque is properly typed now.
Pair is now CompactPair.
Differential Revision: https://phabricator.services.mozilla.com/D213624
Currently, the `<br>` element type -- whether normal `<br>` element or padding
`<br>` element for empty editor or last line -- is managed by the flags of
`nsINode`. Therefore, changing the flag does not cause mutation, so
`IMEContentObserver` cannot observe the type changes. However,
`ContentEventHandler` treats the padding `<br>` elements as invisible.
Therefore, when a `<br>` element becomes a padding one, `IMEContentObserver`
needs to notify IME of atext removed notification, and also when a `<br>`
element becomes a normal one (i.e., visible), `IMEContentObserver` needs to
notify IME of a text added notification.
Therefore, this patch makes `EditorBase` disconnect the `<br>` element
temporarily to make `IMEContentObserver` observable the type change.
Depends on D211698
Differential Revision: https://phabricator.services.mozilla.com/D211699
When 2nd or later line, the method inserts one-line text to start of a `Text`
node following `<br>` which is inserted by the method. Then, splits the `Text`
node to insert another `<br>`. This creates a lot of unnecessary
`SplitNodeTransaction`s and that causes a lot of copying memory operation to
set the data of the right `Text` node.
This patch makes the method creates a `Text` node when inserting a middle line
of inserting text. Therefore, `SplitNodeTransaction` is created at most one
(to split a `Text` node if the caller wants to insert a text middle of it).
Depends on D211697
Differential Revision: https://phabricator.services.mozilla.com/D211698
Currently, the editor of Gecko always unwraps first line of the right child
block after deleting selected range when the range starts in a parent block
and ends in a child block. This behavior is almost same as the other browsers,
but the other browsers deletes only preceding lines of the right child block
(i.e., without unwrapping the first line of the right child block) if the range
starts from start of a preceding line, for example, when deleting
`<div>abc<br>[def<p>g]hi<br>jkl`, Gecko moves "hi" to the parent `<div>`,
but the other browsers keeps it in the child `<p>`.
For emulating this special handling, we need to touch 2 paths.
One is `Backspace` when selection is collapsed at start of the child block. In
this case, only when the preceding line is empty, i.e., there are 2 line breaks
(either `<br>` or `\n` in `white-space: pre-*`), the following break should
be deleted, but the child block should not be touched.
The other is, deleting when selection is not collapsed or `Delete` when
selection is collapsed at immediately before the child block. In the latter
case, `HTMLEditor::HandleDeleteSelection` extends `Selection` using
`nsFrameSelection`. Then, handle it with same path as deleting non-collapsed
range.
The former is handled with `HandleDeleteLineBreak` and
`ComputeRangeToDeleteLineBreak`. The latter is handled with
`HandleDeleteNonCollapsedRange` and `ComputeRangeToDeleteNonCollapsedRange`.
The new handlers use the `ComputeRangeToDelete*`. Therefore, `beforeinput`
reports exactly same range from `getTargetRanges`. However, existing paths
do not use same approach and this patch makes `HandleDeleteNonCollapsedRange`
fall it back to `HandleDeleteNonCollapsedRange`. Therefore, some `if` checks
in `HandleDeleteNonCollapsedRange` are ugly, but I have no better idea to
implement this smarter.
Differential Revision: https://phabricator.services.mozilla.com/D207690
Currently, the editor of Gecko always unwraps first line of the right child
block after deleting selected range when the range starts in a parent block
and ends in a child block. This behavior is almost same as the other browsers,
but the other browsers deletes only preceding lines of the right child block
(i.e., without unwrapping the first line of the right child block) if the range
starts from start of a preceding line, for example, when deleting
`<div>abc<br>[def<p>g]hi<br>jkl`, Gecko moves "hi" to the parent `<div>`,
but the other browsers keeps it in the child `<p>`.
For emulating this special handling, we need to touch 2 paths.
One is `Backspace` when selection is collapsed at start of the child block. In
this case, only when the preceding line is empty, i.e., there are 2 line breaks
(either `<br>` or `\n` in `white-space: pre-*`), the following break should
be deleted, but the child block should not be touched.
The other is, deleting when selection is not collapsed or `Delete` when
selection is collapsed at immediately before the child block. In the latter
case, `HTMLEditor::HandleDeleteSelection` extends `Selection` using
`nsFrameSelection`. Then, handle it with same path as deleting non-collapsed
range.
The former is handled with `HandleDeleteLineBreak` and
`ComputeRangeToDeleteLineBreak`. The latter is handled with
`HandleDeleteNonCollapsedRange` and `ComputeRangeToDeleteNonCollapsedRange`.
The new handlers use the `ComputeRangeToDelete*`. Therefore, `beforeinput`
reports exactly same range from `getTargetRanges`. However, existing paths
do not use same approach and this patch makes `HandleDeleteNonCollapsedRange`
fall it back to `HandleDeleteNonCollapsedRange`. Therefore, some `if` checks
in `HandleDeleteNonCollapsedRange` are ugly, but I have no better idea to
implement this smarter.
Differential Revision: https://phabricator.services.mozilla.com/D207690
`nsDOMAttributeMap::GetAttribute` creates `dom::Attr` so that it's not cheap.
Instead it should access `Element::mAttrs` with the accessors or
`BorrowedAttrInfo`.
Differential Revision: https://phabricator.services.mozilla.com/D208087
It assumes that the range is always starts and ends in different node. This
is true for now, but this will be called with a text node to delete only
preformatted line break. Note that the only caller of it does not need the
text node(s) if it becomes empty. Therefore, this patch makes it remove the
text node in such case.
Note that the test changed in
`input-events-get-target-ranges-deleting-in-list-items.tentative.html` was
wrong and only Firefox passed it because the range description was
`(#text "", 0) - (#text "", 10)` since the text nodes are removed after
deleting the text data of them. Now, they become
`(#text "list-item1", 0) - (#text "list-item2", 10)`.
Depends on D207688
Differential Revision: https://phabricator.services.mozilla.com/D207689
It uses `AsElement()` which always casts itself to `Element*`, however, the
instance may be non-element node if there is no empty parent of `aEmptyContent`.
Fortunately, all callers of this method uses the result as `nsIContent*` to
call `DeleteNodeWithTransaction()`. Therefore, we don't have crash bugs caused
by this.
Depends on D207687
Differential Revision: https://phabricator.services.mozilla.com/D207688
`Point_Deprecated()` is really error-prone since its result meaning is different
whether the scan direction is backward or forward. Therefore, if a caller wants
a point in a text node and the direction is only one of them, we can change it
to use `WSScanResult::PointAtReachedContent()` or
`WSScanResult::PointAfterReachedContent()`.
Depends on D207686
Differential Revision: https://phabricator.services.mozilla.com/D207687
When it's called, it just returns at the reached content node. However, this
does not make sense when it reached a character in the text node.
Depends on D207685
Differential Revision: https://phabricator.services.mozilla.com/D207686
They point the found character point if scanning forward. However, they point
the next character point if scanning backward. Therefore, I don't have any
good idea to name them. Therefore, I rename them to `*_Deprecated()` and the
callers should use better name method later.
Depends on D207684
Differential Revision: https://phabricator.services.mozilla.com/D207685