Bug 1920647 - part 1: Make HTMLEditor won't show Gecko specific editing UI in contenteditable=plaintext-only r=m_kato
Depends on D223277 Differential Revision: https://phabricator.services.mozilla.com/D223669
This commit is contained in:
@@ -135,6 +135,12 @@ NS_IMETHODIMP HTMLEditor::SetAbsolutePositioningEnabled(bool aIsEnabled) {
|
|||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP HTMLEditor::GetIsAbsolutePositioningActive(bool* aIsActive) {
|
||||||
|
MOZ_ASSERT(aIsActive);
|
||||||
|
*aIsActive = !!mAbsolutelyPositionedObject;
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
Result<int32_t, nsresult> HTMLEditor::AddZIndexWithTransaction(
|
Result<int32_t, nsresult> HTMLEditor::AddZIndexWithTransaction(
|
||||||
nsStyledElement& aStyledElement, int32_t aChange) {
|
nsStyledElement& aStyledElement, int32_t aChange) {
|
||||||
if (!aChange) {
|
if (!aChange) {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
#include "mozilla/PresShellInlines.h"
|
#include "mozilla/PresShellInlines.h"
|
||||||
#include "mozilla/dom/BindContext.h"
|
#include "mozilla/dom/BindContext.h"
|
||||||
#include "mozilla/dom/Element.h"
|
#include "mozilla/dom/Element.h"
|
||||||
|
#include "mozilla/dom/ElementInlines.h"
|
||||||
#include "mozilla/dom/EventTarget.h"
|
#include "mozilla/dom/EventTarget.h"
|
||||||
#include "mozilla/mozalloc.h"
|
#include "mozilla/mozalloc.h"
|
||||||
#include "nsAString.h"
|
#include "nsAString.h"
|
||||||
@@ -292,15 +293,23 @@ void HTMLEditor::HideAnonymousEditingUIs() {
|
|||||||
void HTMLEditor::HideAnonymousEditingUIsIfUnnecessary() {
|
void HTMLEditor::HideAnonymousEditingUIsIfUnnecessary() {
|
||||||
// XXX Perhaps, this is wrong approach to hide multiple UIs because
|
// XXX Perhaps, this is wrong approach to hide multiple UIs because
|
||||||
// hiding one UI may causes overwriting existing UI with newly
|
// hiding one UI may causes overwriting existing UI with newly
|
||||||
// created one. In such case, we will leak ovewritten UI.
|
// created one. In such case, we will leak overwritten UI.
|
||||||
if (!IsAbsolutePositionEditorEnabled() && mAbsolutelyPositionedObject) {
|
if (mAbsolutelyPositionedObject) {
|
||||||
|
const Element* const editingHost =
|
||||||
|
mAbsolutelyPositionedObject->GetEditingHost();
|
||||||
|
if (!IsAbsolutePositionEditorEnabled() || !editingHost ||
|
||||||
|
editingHost->IsContentEditablePlainTextOnly()) {
|
||||||
// XXX If we're moving something, we need to cancel or commit the
|
// XXX If we're moving something, we need to cancel or commit the
|
||||||
// operation now.
|
// operation now.
|
||||||
HideGrabberInternal();
|
HideGrabberInternal();
|
||||||
NS_ASSERTION(!mAbsolutelyPositionedObject,
|
NS_ASSERTION(!mAbsolutelyPositionedObject,
|
||||||
"HTMLEditor::HideGrabberInternal() failed, but ignored");
|
"HTMLEditor::HideGrabberInternal() failed, but ignored");
|
||||||
}
|
}
|
||||||
if (!IsInlineTableEditorEnabled() && mInlineEditedCell) {
|
}
|
||||||
|
if (mInlineEditedCell) {
|
||||||
|
const Element* const editingHost = mInlineEditedCell->GetEditingHost();
|
||||||
|
if (!IsInlineTableEditorEnabled() || !editingHost ||
|
||||||
|
editingHost->IsContentEditablePlainTextOnly()) {
|
||||||
// XXX If we're resizing a table element, we need to cancel or commit the
|
// XXX If we're resizing a table element, we need to cancel or commit the
|
||||||
// operation now.
|
// operation now.
|
||||||
HideInlineTableEditingUIInternal();
|
HideInlineTableEditingUIInternal();
|
||||||
@@ -308,7 +317,11 @@ void HTMLEditor::HideAnonymousEditingUIsIfUnnecessary() {
|
|||||||
!mInlineEditedCell,
|
!mInlineEditedCell,
|
||||||
"HTMLEditor::HideInlineTableEditingUIInternal() failed, but ignored");
|
"HTMLEditor::HideInlineTableEditingUIInternal() failed, but ignored");
|
||||||
}
|
}
|
||||||
if (!IsObjectResizerEnabled() && mResizedObject) {
|
}
|
||||||
|
if (mResizedObject) {
|
||||||
|
const Element* const editingHost = mResizedObject->GetEditingHost();
|
||||||
|
if (!IsObjectResizerEnabled() || !editingHost ||
|
||||||
|
editingHost->IsContentEditablePlainTextOnly()) {
|
||||||
// XXX If we're resizing something, we need to cancel or commit the
|
// XXX If we're resizing something, we need to cancel or commit the
|
||||||
// operation now.
|
// operation now.
|
||||||
DebugOnly<nsresult> rvIgnored = HideResizersInternal();
|
DebugOnly<nsresult> rvIgnored = HideResizersInternal();
|
||||||
@@ -318,6 +331,7 @@ void HTMLEditor::HideAnonymousEditingUIsIfUnnecessary() {
|
|||||||
NS_ASSERTION(!mResizedObject,
|
NS_ASSERTION(!mResizedObject,
|
||||||
"HTMLEditor::HideResizersInternal() failed, but ignored");
|
"HTMLEditor::HideResizersInternal() failed, but ignored");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP HTMLEditor::CheckSelectionStateForAnonymousButtons() {
|
NS_IMETHODIMP HTMLEditor::CheckSelectionStateForAnonymousButtons() {
|
||||||
@@ -361,6 +375,13 @@ nsresult HTMLEditor::RefreshEditingUI() {
|
|||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const RefPtr<Element> editingHost =
|
||||||
|
ComputeEditingHost(LimitInBodyElement::No);
|
||||||
|
if (editingHost && editingHost->IsContentEditablePlainTextOnly()) {
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
MOZ_ASSERT(editingHost == selectionContainerElement->GetEditingHost());
|
||||||
|
|
||||||
// what's its tag?
|
// what's its tag?
|
||||||
RefPtr<Element> focusElement = std::move(selectionContainerElement);
|
RefPtr<Element> focusElement = std::move(selectionContainerElement);
|
||||||
nsAtom* focusTagAtom = focusElement->NodeInfo()->NameAtom();
|
nsAtom* focusTagAtom = focusElement->NodeInfo()->NameAtom();
|
||||||
@@ -443,11 +464,9 @@ nsresult HTMLEditor::RefreshEditingUI() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// now, let's display all contextual UI for good
|
// now, let's display all contextual UI for good
|
||||||
nsIContent* hostContent = ComputeEditingHost();
|
|
||||||
|
|
||||||
if (IsObjectResizerEnabled() && focusElement &&
|
if (IsObjectResizerEnabled() && focusElement &&
|
||||||
HTMLEditUtils::IsSimplyEditableNode(*focusElement) &&
|
HTMLEditUtils::IsSimplyEditableNode(*focusElement) &&
|
||||||
focusElement != hostContent) {
|
focusElement != editingHost) {
|
||||||
if (nsGkAtoms::img == focusTagAtom) {
|
if (nsGkAtoms::img == focusTagAtom) {
|
||||||
mResizedObjectIsAnImage = true;
|
mResizedObjectIsAnImage = true;
|
||||||
}
|
}
|
||||||
@@ -468,7 +487,7 @@ nsresult HTMLEditor::RefreshEditingUI() {
|
|||||||
|
|
||||||
if (IsAbsolutePositionEditorEnabled() && absPosElement &&
|
if (IsAbsolutePositionEditorEnabled() && absPosElement &&
|
||||||
HTMLEditUtils::IsSimplyEditableNode(*absPosElement) &&
|
HTMLEditUtils::IsSimplyEditableNode(*absPosElement) &&
|
||||||
absPosElement != hostContent) {
|
absPosElement != editingHost) {
|
||||||
if (mAbsolutelyPositionedObject) {
|
if (mAbsolutelyPositionedObject) {
|
||||||
nsresult rv = RefreshGrabberInternal();
|
nsresult rv = RefreshGrabberInternal();
|
||||||
if (NS_FAILED(rv)) {
|
if (NS_FAILED(rv)) {
|
||||||
@@ -487,7 +506,7 @@ nsresult HTMLEditor::RefreshEditingUI() {
|
|||||||
// XXX Shouldn't we check whether the `<table>` element is editable or not?
|
// XXX Shouldn't we check whether the `<table>` element is editable or not?
|
||||||
if (IsInlineTableEditorEnabled() && cellElement &&
|
if (IsInlineTableEditorEnabled() && cellElement &&
|
||||||
HTMLEditUtils::IsSimplyEditableNode(*cellElement) &&
|
HTMLEditUtils::IsSimplyEditableNode(*cellElement) &&
|
||||||
cellElement != hostContent) {
|
cellElement != editingHost) {
|
||||||
if (mInlineEditedCell) {
|
if (mInlineEditedCell) {
|
||||||
nsresult rv = RefreshInlineTableEditingUIInternal();
|
nsresult rv = RefreshInlineTableEditingUIInternal();
|
||||||
if (NS_FAILED(rv)) {
|
if (NS_FAILED(rv)) {
|
||||||
|
|||||||
@@ -1481,6 +1481,12 @@ NS_IMETHODIMP HTMLEditor::SetObjectResizingEnabled(
|
|||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP HTMLEditor::GetIsObjectResizingActive(bool* aIsActive) {
|
||||||
|
MOZ_ASSERT(aIsActive);
|
||||||
|
*aIsActive = !!mResizedObject;
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
#undef kTopLeft
|
#undef kTopLeft
|
||||||
#undef kTop
|
#undef kTop
|
||||||
#undef kTopRight
|
#undef kTopRight
|
||||||
|
|||||||
@@ -31,6 +31,12 @@ NS_IMETHODIMP HTMLEditor::GetInlineTableEditingEnabled(bool* aIsEnabled) {
|
|||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP HTMLEditor::GetIsInlineTableEditingActive(bool* aIsActive) {
|
||||||
|
MOZ_ASSERT(aIsActive);
|
||||||
|
*aIsActive = !!mInlineEditedCell;
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
nsresult HTMLEditor::ShowInlineTableEditingUIInternal(Element& aCellElement) {
|
nsresult HTMLEditor::ShowInlineTableEditingUIInternal(Element& aCellElement) {
|
||||||
if (NS_WARN_IF(!HTMLEditUtils::IsTableCell(&aCellElement))) {
|
if (NS_WARN_IF(!HTMLEditUtils::IsTableCell(&aCellElement))) {
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
|
|||||||
@@ -444,6 +444,8 @@ skip-if = ["os == 'android'"] # Needs interaction with the scrollbar
|
|||||||
|
|
||||||
["test_dragend_target.html"]
|
["test_dragend_target.html"]
|
||||||
|
|
||||||
|
["test_editing_UI_in_plaintext-only.html"]
|
||||||
|
|
||||||
["test_execCommandPaste_noTarget.html"]
|
["test_execCommandPaste_noTarget.html"]
|
||||||
|
|
||||||
["test_focus_caret_navigation_between_nested_editors.html"]
|
["test_focus_caret_navigation_between_nested_editors.html"]
|
||||||
|
|||||||
@@ -0,0 +1,95 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title></title>
|
||||||
|
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||||
|
<script src="/tests/SimpleTest/EventUtils.js"></script>
|
||||||
|
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||||
|
<script>
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
SimpleTest.waitForExplicitFinish();
|
||||||
|
SimpleTest.waitForFocus(async () => {
|
||||||
|
await SpecialPowers.pushPrefEnv({
|
||||||
|
set: [["dom.element.contenteditable.plaintext-only.enabled", true]],
|
||||||
|
});
|
||||||
|
function waitForTick() {
|
||||||
|
return new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve)));
|
||||||
|
}
|
||||||
|
await (async () => {
|
||||||
|
const editingHost = document.getElementById("testAbs");
|
||||||
|
editingHost.contentEditable = "plaintext-only";
|
||||||
|
editingHost.focus();
|
||||||
|
document.execCommand("enableAbsolutePositionEditing", false, "true");
|
||||||
|
const target = editingHost.querySelector("div[style]");
|
||||||
|
synthesizeMouseAtCenter(target, {});
|
||||||
|
await waitForTick();
|
||||||
|
is(
|
||||||
|
getHTMLEditor().isAbsolutePositioningActive,
|
||||||
|
false,
|
||||||
|
"The absolutely positioned element positioner UI should not be visible"
|
||||||
|
);
|
||||||
|
document.execCommand("enableAbsolutePositionEditing", false, "false");
|
||||||
|
editingHost.remove();
|
||||||
|
await waitForTick();
|
||||||
|
})();
|
||||||
|
await (async () => {
|
||||||
|
const editingHost = document.getElementById("testTable");
|
||||||
|
editingHost.contentEditable = "plaintext-only";
|
||||||
|
editingHost.focus();
|
||||||
|
document.execCommand("enableInlineTableEditing", false, "true");
|
||||||
|
const target = editingHost.querySelector("td");
|
||||||
|
synthesizeMouseAtCenter(target, {});
|
||||||
|
await waitForTick();
|
||||||
|
is(
|
||||||
|
getHTMLEditor().isInlineTableEditingActive,
|
||||||
|
false,
|
||||||
|
"The inline table editing UI should not be visible"
|
||||||
|
);
|
||||||
|
document.execCommand("enableInlineTableEditing", false, "false");
|
||||||
|
editingHost.remove();
|
||||||
|
await waitForTick();
|
||||||
|
})();
|
||||||
|
await (async () => {
|
||||||
|
const editingHost = document.getElementById("testResizer");
|
||||||
|
editingHost.contentEditable = "plaintext-only";
|
||||||
|
editingHost.focus();
|
||||||
|
document.execCommand("enableObjectResizing", false, "true");
|
||||||
|
const target = editingHost.querySelector("img");
|
||||||
|
synthesizeMouseAtCenter(target, {});
|
||||||
|
await waitForTick();
|
||||||
|
is(
|
||||||
|
getHTMLEditor().isObjectResizingActive,
|
||||||
|
false,
|
||||||
|
"The image resizer UI should not be visible"
|
||||||
|
);
|
||||||
|
document.execCommand("enableObjectResizing", false, "false");
|
||||||
|
editingHost.remove();
|
||||||
|
await waitForTick();
|
||||||
|
})();
|
||||||
|
SimpleTest.finish();
|
||||||
|
});
|
||||||
|
|
||||||
|
function getHTMLEditor() {
|
||||||
|
const Ci = SpecialPowers.Ci;
|
||||||
|
const editingSession = SpecialPowers.wrap(window).docShell.editingSession;
|
||||||
|
return editingSession.getEditorForWindow(window)
|
||||||
|
.QueryInterface(Ci.nsIHTMLAbsPosEditor)
|
||||||
|
.QueryInterface(Ci.nsIHTMLInlineTableEditor)
|
||||||
|
.QueryInterface(Ci.nsIHTMLObjectResizer);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="testAbs" style="position: absolute; width: 150px; height: 150px">
|
||||||
|
<div style="position: absolute; width: 100px; height: 100px"><br></div>
|
||||||
|
</div>
|
||||||
|
<div id="testTable">
|
||||||
|
<table><td><br></td></table>
|
||||||
|
</div>
|
||||||
|
<div id="testResizer">
|
||||||
|
<img src="green.png">
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -15,6 +15,11 @@ interface nsIHTMLAbsPosEditor : nsISupports
|
|||||||
[setter_can_run_script]
|
[setter_can_run_script]
|
||||||
attribute boolean absolutePositioningEnabled;
|
attribute boolean absolutePositioningEnabled;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* true if the grabber to drag the absolutly positioned element is visible.
|
||||||
|
*/
|
||||||
|
[infallible] readonly attribute boolean isAbsolutePositioningActive;
|
||||||
|
|
||||||
|
|
||||||
/* Utility methods */
|
/* Utility methods */
|
||||||
|
|
||||||
|
|||||||
@@ -17,4 +17,9 @@ interface nsIHTMLInlineTableEditor : nsISupports
|
|||||||
*/
|
*/
|
||||||
[setter_can_run_script]
|
[setter_can_run_script]
|
||||||
attribute boolean inlineTableEditingEnabled;
|
attribute boolean inlineTableEditingEnabled;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* true if the inline table editing UI is visible.
|
||||||
|
*/
|
||||||
|
[infallible] readonly attribute boolean isInlineTableEditingActive;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -29,6 +29,11 @@ interface nsIHTMLObjectResizer : nsISupports
|
|||||||
[setter_can_run_script]
|
[setter_can_run_script]
|
||||||
attribute boolean objectResizingEnabled;
|
attribute boolean objectResizingEnabled;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* true if the object resizing UI is visible.
|
||||||
|
*/
|
||||||
|
[infallible] readonly attribute boolean isObjectResizingActive;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hide resizers if they are visible. If this is called while there is no
|
* Hide resizers if they are visible. If this is called while there is no
|
||||||
* visible resizers, this does not throw exception, just does nothing.
|
* visible resizers, this does not throw exception, just does nothing.
|
||||||
|
|||||||
Reference in New Issue
Block a user