Bug 1943542 - Remove contenteditable / EditorOverride sheet. r=masayuki

Move the relevant bits to html.css or other UA sheets, and use faster
things than attribute selectors (read: classes) for resizers and such
things.

The hidden class can just be a hidden attribute since we don't otherwise
style these.

The cursor can just be set at the resizer / grabber creation time.

One known behavior change other than this is not styling using the
_moz_abspos attribute. The reasoning for this is that these are content
nodes, and in general having these kinds of rules for content nodes
isn't great. E.g. these two pages behave differently:

 * data:text/html,<div _moz_abspos=black>boo</div>
 * data:text/html,<div _moz_abspos=black>boo</div><div contenteditable></div>

We could use the style attribute for this, I suppose, just like we set
the position or what not, but then we need to save / restore as needed
or something. I'd rather not change the stacking order or backgrounds or
what not, too.

If you feel very strongly about that piece of functionality of the
abspos editor I could try to restore it somehow. But I honestly rather
not.

The rest of the stuff should just work.

Differential Revision: https://phabricator.services.mozilla.com/D235319
This commit is contained in:
Emilio Cobos Álvarez
2025-01-26 12:31:18 +00:00
parent 5fcc6c93b8
commit d5d33843fb
18 changed files with 240 additions and 708 deletions

View File

@@ -129,6 +129,7 @@ var allowlist = [
// Intentionally unreferenced, see bug 1941134
{ file: "resource://gre/res/designmode.css" },
{ file: "resource://gre/res/EditorOverride.css" },
// The l10n build system can't package string files only for some platforms.
// See bug 1339424 for why this is hard to fix.

View File

@@ -1371,7 +1371,6 @@ Document::Document(const char* aContentType)
mIsGoingAway(false),
mStyleSetFilled(false),
mQuirkSheetAdded(false),
mContentEditableSheetAdded(false),
mMayHaveTitleElement(false),
mDOMLoadingSet(false),
mDOMInteractiveSet(false),
@@ -3304,41 +3303,6 @@ void Document::FillStyleSet() {
mStyleSetFilled = true;
}
void Document::RemoveContentEditableStyleSheet() {
MOZ_ASSERT(IsHTMLOrXHTML());
ServoStyleSet& styleSet = EnsureStyleSet();
auto* cache = GlobalStyleSheetCache::Singleton();
bool changed = false;
if (mContentEditableSheetAdded) {
styleSet.RemoveStyleSheet(*cache->ContentEditableSheet());
mContentEditableSheetAdded = false;
changed = true;
}
if (changed) {
MOZ_ASSERT(mStyleSetFilled);
ApplicableStylesChanged();
}
}
void Document::AddContentEditableStyleSheetToStyleSet() {
MOZ_ASSERT(IsHTMLOrXHTML());
MOZ_DIAGNOSTIC_ASSERT(mStyleSetFilled,
"Caller should ensure we're being rendered");
ServoStyleSet& styleSet = EnsureStyleSet();
auto* cache = GlobalStyleSheetCache::Singleton();
bool changed = false;
if (!mContentEditableSheetAdded) {
styleSet.AppendStyleSheet(*cache->ContentEditableSheet());
mContentEditableSheetAdded = true;
changed = true;
}
if (changed) {
ApplicableStylesChanged();
}
}
void Document::FillStyleSetDocumentSheets() {
ServoStyleSet& styleSet = EnsureStyleSet();
MOZ_ASSERT(styleSet.SheetCount(StyleOrigin::Author) == 0,
@@ -6070,9 +6034,6 @@ void Document::SetNotifyFormOrPasswordRemoved(bool aShouldNotify) {
void Document::TearingDownEditor() {
if (IsEditingOn()) {
mEditingState = EditingState::eTearingDown;
if (IsHTMLOrXHTML()) {
RemoveContentEditableStyleSheet();
}
}
}
@@ -6278,13 +6239,6 @@ nsresult Document::EditingStateChanged() {
MOZ_ASSERT(mStyleSetFilled);
// Before making this window editable, we need to modify UA style sheet
// because new style may change whether focused element will be focusable
// or not.
if (IsHTMLOrXHTML()) {
AddContentEditableStyleSheetToStyleSet();
}
if (designMode) {
// designMode is being turned on (overrides contentEditable).
spellRecheckAll = oldState == EditingState::eContentEditable;
@@ -7450,7 +7404,6 @@ void Document::DeletePresShell() {
mStyleSet->ShellDetachedFromDocument();
mStyleSetFilled = false;
mQuirkSheetAdded = false;
mContentEditableSheetAdded = false;
}
void Document::DisallowBFCaching(uint32_t aStatus) {

View File

@@ -4276,8 +4276,6 @@ class Document : public nsINode,
// FIXME(emilio): Can SVG documents be in quirks mode anyway?
return mCompatMode == eCompatibility_NavQuirks && !IsSVGDocument();
}
void AddContentEditableStyleSheetToStyleSet();
void RemoveContentEditableStyleSheet();
void AddStyleSheetToStyleSets(StyleSheet&);
void RemoveStyleSheetFromStyleSets(StyleSheet&);
void NotifyStyleSheetApplicableStateChanged();
@@ -4834,9 +4832,6 @@ class Document : public nsINode,
// Whether we have a quirks mode stylesheet in the style set.
bool mQuirkSheetAdded : 1;
// Whether we have a contenteditable.css stylesheet in the style set.
bool mContentEditableSheetAdded : 1;
// True if this document has ever had an HTML or SVG <title> element
// bound to it
bool mMayHaveTitleElement : 1;

View File

@@ -2,277 +2,4 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* Styles to alter look of things in the Editor content window
* that should NOT be removed when we display in completely WYSIWYG
* "Browser Preview" mode.
* Anything that should change, like appearance of table borders
* and Named Anchors, should be placed in EditorContent.css instead of here.
*/
/* Primary cursor is text I-beam */
::-moz-canvas, a:link {
cursor: text;
}
/* Use default arrow over objects with size that
are selected when clicked on.
Override the browser's pointer cursor over links
*/
img, img[usemap], area,
object, object[usemap],
applet, hr, button, input, textarea, select,
a:link img, a:visited img, a:active img,
a[name]:-moz-only-whitespace {
cursor: default;
}
a:visited, a:active {
cursor: text;
}
/* We suppress user/author's prefs for link underline,
so we must set explicitly. This isn't good!
*/
a:link {
color: -moz-hyperlinktext;
}
label {
user-select: all !important;
}
option {
user-select: text !important;
}
/* the following rules are for Image Resizing */
span[\_moz_anonclass="mozResizer"] {
width: 5px;
height: 5px;
position: absolute;
border: 1px black solid;
background-color: white;
user-select: none;
z-index: 2147483646; /* max value -1 for this property */
}
/* we can't use :active below */
span[\_moz_anonclass="mozResizer"][\_moz_activated],
span[\_moz_anonclass="mozResizer"]:hover {
background-color: black;
}
span[\_moz_anonclass="mozResizer"].hidden,
span[\_moz_anonclass="mozResizingShadow"].hidden,
img[\_moz_anonclass="mozResizingShadow"].hidden,
span[\_moz_anonclass="mozGrabber"].hidden,
span[\_moz_anonclass="mozResizingInfo"].hidden,
a[\_moz_anonclass="mozTableRemoveRow"].hidden,
a[\_moz_anonclass="mozTableRemoveColumn"].hidden {
display: none !important;
}
span[\_moz_anonclass="mozResizer"][anonlocation="nw"] {
cursor: nw-resize;
}
span[\_moz_anonclass="mozResizer"][anonlocation="n"] {
cursor: n-resize;
}
span[\_moz_anonclass="mozResizer"][anonlocation="ne"] {
cursor: ne-resize;
}
span[\_moz_anonclass="mozResizer"][anonlocation="w"] {
cursor: w-resize;
}
span[\_moz_anonclass="mozResizer"][anonlocation="e"] {
cursor: e-resize;
}
span[\_moz_anonclass="mozResizer"][anonlocation="sw"] {
cursor: sw-resize;
}
span[\_moz_anonclass="mozResizer"][anonlocation="s"] {
cursor: s-resize;
}
span[\_moz_anonclass="mozResizer"][anonlocation="se"] {
cursor: se-resize;
}
span[\_moz_anonclass="mozResizingShadow"],
img[\_moz_anonclass="mozResizingShadow"] {
outline: thin dashed black;
user-select: none;
opacity: 0.5;
position: absolute;
z-index: 2147483647; /* max value for this property */
}
span[\_moz_anonclass="mozResizingInfo"] {
font-family: sans-serif;
font-size: x-small;
color: black;
background-color: #d0d0d0;
border: ridge 2px #d0d0d0;
padding: 2px;
position: absolute;
z-index: 2147483647; /* max value for this property */
}
img[\_moz_resizing] {
outline: thin solid black;
}
*[\_moz_abspos] {
outline: silver ridge 2px;
z-index: 2147483645 !important; /* max value -2 for this property */
}
*[\_moz_abspos="white"] {
background-color: white !important;
}
*[\_moz_abspos="black"] {
background-color: black !important;
}
span[\_moz_anonclass="mozGrabber"] {
outline: ridge 2px silver;
padding: 2px;
position: absolute;
width: 12px;
height: 12px;
background-image: url("resource://gre/res/grabber.gif");
background-repeat: no-repeat;
background-position: center center;
user-select: none;
cursor: move;
z-index: 2147483647; /* max value for this property */
}
/* INLINE TABLE EDITING */
a[\_moz_anonclass="mozTableAddColumnBefore"] {
position: absolute;
z-index: 2147483647; /* max value for this property */
text-decoration: none !important;
border: none 0 !important;
width: 4px;
height: 8px;
background-image: url("resource://gre/res/table-add-column-before.gif");
background-repeat: no-repeat;
background-position: center center;
user-select: none !important;
}
a[\_moz_anonclass="mozTableAddColumnBefore"]:hover {
background-image: url("resource://gre/res/table-add-column-before-hover.gif");
}
a[\_moz_anonclass="mozTableAddColumnBefore"]:active {
background-image: url("resource://gre/res/table-add-column-before-active.gif");
}
a[\_moz_anonclass="mozTableAddColumnAfter"] {
position: absolute;
z-index: 2147483647; /* max value for this property */
text-decoration: none !important;
border: none 0 !important;
width: 4px;
height: 8px;
background-image: url("resource://gre/res/table-add-column-after.gif");
background-repeat: no-repeat;
background-position: center center;
user-select: none !important;
}
a[\_moz_anonclass="mozTableAddColumnAfter"]:hover {
background-image: url("resource://gre/res/table-add-column-after-hover.gif");
}
a[\_moz_anonclass="mozTableAddColumnAfter"]:active {
background-image: url("resource://gre/res/table-add-column-after-active.gif");
}
a[\_moz_anonclass="mozTableRemoveColumn"] {
position: absolute;
z-index: 2147483647; /* max value for this property */
text-decoration: none !important;
border: none 0 !important;
width: 8px;
height: 8px;
background-image: url("resource://gre/res/table-remove-column.gif");
background-repeat: no-repeat;
background-position: center center;
user-select: none !important;
}
a[\_moz_anonclass="mozTableRemoveColumn"]:hover {
background-image: url("resource://gre/res/table-remove-column-hover.gif");
}
a[\_moz_anonclass="mozTableRemoveColumn"]:active {
background-image: url("resource://gre/res/table-remove-column-active.gif");
}
a[\_moz_anonclass="mozTableAddRowBefore"] {
position: absolute;
z-index: 2147483647; /* max value for this property */
text-decoration: none !important;
border: none 0 !important;
width: 8px;
height: 4px;
background-image: url("resource://gre/res/table-add-row-before.gif");
background-repeat: no-repeat;
background-position: center center;
user-select: none !important;
}
a[\_moz_anonclass="mozTableAddRowBefore"]:hover {
background-image: url("resource://gre/res/table-add-row-before-hover.gif");
}
a[\_moz_anonclass="mozTableAddRowBefore"]:active {
background-image: url("resource://gre/res/table-add-row-before-active.gif");
}
a[\_moz_anonclass="mozTableAddRowAfter"] {
position: absolute;
z-index: 2147483647; /* max value for this property */
text-decoration: none !important;
border: none 0 !important;
width: 8px;
height: 4px;
background-image: url("resource://gre/res/table-add-row-after.gif");
background-repeat: no-repeat;
background-position: center center;
user-select: none !important;
}
a[\_moz_anonclass="mozTableAddRowAfter"]:hover {
background-image: url("resource://gre/res/table-add-row-after-hover.gif");
}
a[\_moz_anonclass="mozTableAddRowAfter"]:active {
background-image: url("resource://gre/res/table-add-row-after-active.gif");
}
a[\_moz_anonclass="mozTableRemoveRow"] {
position: absolute;
z-index: 2147483647; /* max value for this property */
text-decoration: none !important;
border: none 0 !important;
width: 8px;
height: 8px;
background-image: url("resource://gre/res/table-remove-row.gif");
background-repeat: no-repeat;
background-position: center center;
user-select: none !important;
}
a[\_moz_anonclass="mozTableRemoveRow"]:hover {
background-image: url("resource://gre/res/table-remove-row-hover.gif");
}
a[\_moz_anonclass="mozTableRemoveRow"]:active {
background-image: url("resource://gre/res/table-remove-row-active.gif");
}
/* EditorOverride.css is empty, see bug 1941134 */

View File

@@ -160,20 +160,19 @@ ManualNACPtr HTMLEditor::CreateAnonymousElement(nsAtom* aTag,
return nullptr;
}
// add the "hidden" class if needed
if (aIsCreatedHidden) {
nsresult rv = newElement->SetAttr(kNameSpaceID_None, nsGkAtoms::_class,
u"hidden"_ns, true);
nsresult rv =
newElement->SetAttr(kNameSpaceID_None, nsGkAtoms::hidden, u""_ns, true);
if (NS_FAILED(rv)) {
NS_WARNING("Element::SetAttr(nsGkAtoms::_class, hidden) failed");
NS_WARNING("Element::SetAttr(nsGkAtoms::hidden, ...) failed");
return nullptr;
}
}
// add an _moz_anonclass attribute if needed
if (!aAnonClass.IsEmpty()) {
nsresult rv = newElement->SetAttr(
kNameSpaceID_None, nsGkAtoms::_moz_anonclass, aAnonClass, true);
nsresult rv = newElement->SetAttr(kNameSpaceID_None, nsGkAtoms::_class,
aAnonClass, true);
if (NS_FAILED(rv)) {
NS_WARNING("Element::SetAttr(nsGkAtoms::_moz_anonclass) failed");
return nullptr;

View File

@@ -402,16 +402,6 @@ nsresult HTMLEditor::Init(Document& aDocument,
// init the type-in state
mPendingStylesToApplyToNewContent = new PendingStyles();
if (!IsInteractionAllowed()) {
nsCOMPtr<nsIURI> uaURI;
rv = NS_NewURI(getter_AddRefs(uaURI),
"resource://gre/res/EditorOverride.css");
NS_ENSURE_SUCCESS(rv, rv);
rv = document->LoadAdditionalStyleSheet(Document::eAgentSheet, uaURI);
NS_ENSURE_SUCCESS(rv, rv);
}
AutoEditActionDataSetter editActionData(*this, EditAction::eInitializing);
if (NS_WARN_IF(!editActionData.CanHandle())) {
return NS_ERROR_FAILURE;
@@ -464,15 +454,6 @@ void HTMLEditor::PreDestroy() {
RefPtr<Document> document = GetDocument();
if (document) {
document->RemoveMutationObserver(this);
if (!IsInteractionAllowed()) {
nsCOMPtr<nsIURI> uaURI;
nsresult rv = NS_NewURI(getter_AddRefs(uaURI),
"resource://gre/res/EditorOverride.css");
if (NS_SUCCEEDED(rv)) {
document->RemoveAdditionalStyleSheet(Document::eAgentSheet, uaURI);
}
}
}
// Clean up after our anonymous content -- we don't want these nodes to

View File

@@ -4384,19 +4384,19 @@ class HTMLEditor final : public EditorBase,
/**
* Returns an anonymous Element of type aTag,
* child of aParentContent. If aIsCreatedHidden is true, the class
* "hidden" is added to the created element. If aAnonClass is not
* the empty string, it becomes the value of the attribute "_moz_anonclass"
* "hidden" is added to the created element. If aClass is not the empty
* string, it becomes the value of the class attribute
* @return a Element
* @param aTag [IN] desired type of the element to create
* @param aParentContent [IN] the parent node of the created anonymous
* element
* @param aAnonClass [IN] contents of the _moz_anonclass attribute
* @param aClass [IN] contents of the _moz_anonclass attribute
* @param aIsCreatedHidden [IN] a boolean specifying if the class "hidden"
* is to be added to the created anonymous
* element
*/
ManualNACPtr CreateAnonymousElement(nsAtom* aTag, nsIContent& aParentContent,
const nsAString& aAnonClass,
const nsAString& aClass,
bool aIsCreatedHidden);
/**

View File

@@ -20,13 +20,15 @@
#include "mozilla/dom/Event.h"
#include "mozilla/dom/MouseEvent.h"
#include "mozilla/dom/EventTarget.h"
#include "nsAtom.h"
#include "nsAString.h"
#include "nsCOMPtr.h"
#include "nsDebug.h"
#include "nsDOMTokenList.h"
#include "nsError.h"
#include "nsGkAtoms.h"
#include "nsAtom.h"
#include "nsIContent.h"
#include "nsICSSDeclaration.h"
#include "nsID.h"
#include "mozilla/dom/Document.h"
#include "nsISupportsUtils.h"
@@ -73,7 +75,6 @@ ManualNACPtr HTMLEditor::CreateResizer(int16_t aLocation,
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rvIgnored),
"EventTarget::AddEventListener(mousedown) failed, but ignored");
nsAutoString locationStr;
switch (aLocation) {
case nsIHTMLObjectResizer::eTopLeft:
@@ -109,6 +110,12 @@ ManualNACPtr HTMLEditor::CreateResizer(int16_t aLocation,
NS_WARNING("Element::SetAttr(nsGkAtoms::anonlocation) failed");
return nullptr;
}
nsAutoString cursor = u"cursor: "_ns + locationStr + u"-resize"_ns;
if (NS_FAILED(resizer->SetAttr(kNameSpaceID_None, nsGkAtoms::style, cursor,
true))) {
NS_WARNING("Element::SetAttr(nsGkAtoms::style) failed");
return nullptr;
}
return resizer;
}
@@ -509,14 +516,6 @@ nsresult HTMLEditor::ShowResizersInternal(Element& aResizedElement) {
}
MOZ_ASSERT(mResizedObject == &aResizedElement);
// XXX Even when it failed to add event listener, should we need to set
// _moz_resizing attribute?
DebugOnly<nsresult> rvIgnored = aResizedElement.SetAttr(
kNameSpaceID_None, nsGkAtoms::_moz_resizing, u"true"_ns, true);
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rvIgnored),
"Element::SetAttr(nsGkAtoms::_moz_resizing, true) failed, but ignored");
return NS_OK;
} while (true);
@@ -605,20 +604,9 @@ nsresult HTMLEditor::HideResizersInternal() {
// Remove active state of a resizer.
if (activatedHandle) {
DebugOnly<nsresult> rvIgnored = activatedHandle->UnsetAttr(
kNameSpaceID_None, nsGkAtoms::_moz_activated, true);
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rvIgnored),
"Element::UnsetAttr(nsGkAtoms::_moz_activated) failed, but ignored");
activatedHandle->ClassList()->Remove(u"active"_ns, IgnoreErrors());
}
// Remove resizing state of the target element.
DebugOnly<nsresult> rvIgnored = resizedObject->UnsetAttr(
kNameSpaceID_None, nsGkAtoms::_moz_resizing, true);
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rvIgnored),
"Element::UnsetAttr(nsGkAtoms::_moz_resizing) failed, but ignored");
if (!mEventListener) {
return NS_OK;
}
@@ -648,31 +636,27 @@ nsresult HTMLEditor::HideResizersInternal() {
void HTMLEditor::HideShadowAndInfo() {
if (mResizingShadow) {
DebugOnly<nsresult> rvIgnored = mResizingShadow->SetAttr(
kNameSpaceID_None, nsGkAtoms::_class, u"hidden"_ns, true);
kNameSpaceID_None, nsGkAtoms::hidden, u""_ns, true);
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rvIgnored),
"Element::SetAttr(nsGkAtoms::_class, hidden) failed, but ignored");
"Element::SetAttr(nsGkAtoms::hidden) failed, but ignored");
}
if (mResizingInfo) {
DebugOnly<nsresult> rvIgnored = mResizingInfo->SetAttr(
kNameSpaceID_None, nsGkAtoms::_class, u"hidden"_ns, true);
kNameSpaceID_None, nsGkAtoms::hidden, u""_ns, true);
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rvIgnored),
"Element::SetAttr(nsGkAtoms::_class, hidden) failed, but ignored");
"Element::SetAttr(nsGkAtoms::hidden, hidden) failed, but ignored");
}
}
nsresult HTMLEditor::StartResizing(Element& aHandleElement) {
mIsResizing = true;
mActivatedHandle = &aHandleElement;
DebugOnly<nsresult> rvIgnored = mActivatedHandle->SetAttr(
kNameSpaceID_None, nsGkAtoms::_moz_activated, u"true"_ns, true);
mActivatedHandle->ClassList()->Add(u"active"_ns, IgnoreErrors());
if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rvIgnored),
"Element::SetAttr(nsGkAtoms::_moz_activated, true) failed");
// do we want to preserve ratio or not?
const bool preserveRatio = HTMLEditUtils::IsImage(mResizedObject);
@@ -700,13 +684,13 @@ nsresult HTMLEditor::StartResizing(Element& aHandleElement) {
}
// make the shadow appear
rvIgnored =
mResizingShadow->UnsetAttr(kNameSpaceID_None, nsGkAtoms::_class, true);
DebugOnly<nsresult> rvIgnored =
mResizingShadow->UnsetAttr(kNameSpaceID_None, nsGkAtoms::hidden, true);
if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}
NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
"Element::UnsetAttr(nsGkAtoms::_class) failed");
"Element::UnsetAttr(nsGkAtoms::hidden) failed");
// position it
if (RefPtr<nsStyledElement> resizingShadowStyledElement =
@@ -755,10 +739,9 @@ nsresult HTMLEditor::StartToDragResizerOrHandleDragGestureOnGrabber(
MOZ_ASSERT(!aMouseDownEvent.DefaultPrevented());
MOZ_ASSERT(aMouseDownEvent.WidgetEventPtr()->mMessage == eMouseDown);
nsAutoString anonclass;
aEventTargetElement.GetAttr(nsGkAtoms::_moz_anonclass, anonclass);
nsDOMTokenList& classList = *aEventTargetElement.ClassList();
if (anonclass.EqualsLiteral("mozResizer")) {
if (classList.Contains(u"mozResizer"_ns)) {
AutoEditActionDataSetter editActionData(*this,
EditAction::eResizingElement);
if (NS_WARN_IF(!editActionData.CanHandle())) {
@@ -776,7 +759,7 @@ nsresult HTMLEditor::StartToDragResizerOrHandleDragGestureOnGrabber(
return EditorBase::ToGenericNSResult(rv);
}
if (anonclass.EqualsLiteral("mozGrabber")) {
if (classList.Contains(u"mozGrabber"_ns)) {
AutoEditActionDataSetter editActionData(*this, EditAction::eMovingElement);
if (NS_WARN_IF(!editActionData.CanHandle())) {
return NS_ERROR_NOT_INITIALIZED;
@@ -838,10 +821,9 @@ nsresult HTMLEditor::StopDraggingResizerOrGrabberAt(
if (mIsMoving) {
DebugOnly<nsresult> rvIgnored = mPositioningShadow->SetAttr(
kNameSpaceID_None, nsGkAtoms::_class, u"hidden"_ns, true);
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rvIgnored),
"Element::SetAttr(nsGkAtoms::_class, hidden) failed");
kNameSpaceID_None, nsGkAtoms::hidden, u""_ns, true);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
"Element::SetAttr(nsGkAtoms::hidden) failed");
if (rv != NS_ERROR_EDITOR_ACTION_CANCELED) {
SetFinalPosition(aClientPoint.x, aClientPoint.y);
}
@@ -974,9 +956,9 @@ nsresult HTMLEditor::SetResizingInfoPosition(int32_t aX, int32_t aY, int32_t aW,
}
nsresult rv =
mResizingInfo->UnsetAttr(kNameSpaceID_None, nsGkAtoms::_class, true);
mResizingInfo->UnsetAttr(kNameSpaceID_None, nsGkAtoms::hidden, true);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"Element::UnsetAttr(nsGkAtoms::_class) failed");
"Element::UnsetAttr(nsGkAtoms::hidden) failed");
return rv;
}
@@ -1233,11 +1215,7 @@ nsresult HTMLEditor::SetFinalSizeWithTransaction(int32_t aX, int32_t aY) {
}
if (mActivatedHandle) {
DebugOnly<nsresult> rvIgnored = mActivatedHandle->UnsetAttr(
kNameSpaceID_None, nsGkAtoms::_moz_activated, true);
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rvIgnored),
"Element::UnsetAttr(nsGkAtoms::_moz_activated) failed, but ignored");
mActivatedHandle->ClassList()->Remove(u"active"_ns, IgnoreErrors());
mActivatedHandle = nullptr;
}

View File

@@ -210,10 +210,10 @@ void HTMLEditor::HideInlineTableEditingUIInternal() {
}
nsresult HTMLEditor::DoInlineTableEditingAction(const Element& aElement) {
nsAutoString anonclass;
aElement.GetAttr(nsGkAtoms::_moz_anonclass, anonclass);
nsAutoString classList;
aElement.GetAttr(nsGkAtoms::_class, classList);
if (!StringBeginsWith(anonclass, u"mozTable"_ns)) {
if (!StringBeginsWith(classList, u"mozTable"_ns)) {
return NS_OK;
}
@@ -245,7 +245,7 @@ nsresult HTMLEditor::DoInlineTableEditingAction(const Element& aElement) {
bool hideUI = false;
bool hideResizersWithInlineTableUI = (mResizedObject == tableElement);
if (anonclass.EqualsLiteral("mozTableAddColumnBefore")) {
if (classList.EqualsLiteral("mozTableAddColumnBefore")) {
AutoEditActionDataSetter editActionData(*this,
EditAction::eInsertTableColumn);
nsresult rv = editActionData.CanHandleAndMaybeDispatchBeforeInputEvent();
@@ -262,7 +262,7 @@ nsresult HTMLEditor::DoInlineTableEditingAction(const Element& aElement) {
NS_SUCCEEDED(rvIgnored),
"HTMLEditor::InsertTableColumnsWithTransaction("
"EditorDOMPoint(mInlineEditedCell), 1) failed, but ignored");
} else if (anonclass.EqualsLiteral("mozTableAddColumnAfter")) {
} else if (classList.EqualsLiteral("mozTableAddColumnAfter")) {
AutoEditActionDataSetter editActionData(*this,
EditAction::eInsertTableColumn);
nsresult rv = editActionData.CanHandleAndMaybeDispatchBeforeInputEvent();
@@ -290,7 +290,7 @@ nsresult HTMLEditor::DoInlineTableEditingAction(const Element& aElement) {
NS_SUCCEEDED(rvIgnored),
"HTMLEditor::InsertTableColumnsWithTransaction("
"EditorDOMPoint(nextCellElement), 1) failed, but ignored");
} else if (anonclass.EqualsLiteral("mozTableAddRowBefore")) {
} else if (classList.EqualsLiteral("mozTableAddRowBefore")) {
AutoEditActionDataSetter editActionData(*this,
EditAction::eInsertTableRowElement);
nsresult rv = editActionData.CanHandleAndMaybeDispatchBeforeInputEvent();
@@ -307,7 +307,7 @@ nsresult HTMLEditor::DoInlineTableEditingAction(const Element& aElement) {
NS_SUCCEEDED(rvIgnored),
"HTMLEditor::InsertTableRowsWithTransaction(targetCellElement, 1, "
"InsertPosition::eBeforeSelectedCell) failed, but ignored");
} else if (anonclass.EqualsLiteral("mozTableAddRowAfter")) {
} else if (classList.EqualsLiteral("mozTableAddRowAfter")) {
AutoEditActionDataSetter editActionData(*this,
EditAction::eInsertTableRowElement);
nsresult rv = editActionData.CanHandleAndMaybeDispatchBeforeInputEvent();
@@ -324,7 +324,7 @@ nsresult HTMLEditor::DoInlineTableEditingAction(const Element& aElement) {
NS_SUCCEEDED(rvIgnored),
"HTMLEditor::InsertTableRowsWithTransaction(targetCellElement, 1, "
"InsertPosition::eAfterSelectedCell) failed, but ignored");
} else if (anonclass.EqualsLiteral("mozTableRemoveColumn")) {
} else if (classList.EqualsLiteral("mozTableRemoveColumn")) {
AutoEditActionDataSetter editActionData(*this,
EditAction::eRemoveTableColumn);
nsresult rv = editActionData.CanHandleAndMaybeDispatchBeforeInputEvent();
@@ -341,7 +341,7 @@ nsresult HTMLEditor::DoInlineTableEditingAction(const Element& aElement) {
"HTMLEditor::DeleteSelectedTableColumnsWithTransaction(1) failed, but "
"ignored");
hideUI = (colCount == 1);
} else if (anonclass.EqualsLiteral("mozTableRemoveRow")) {
} else if (classList.EqualsLiteral("mozTableRemoveRow")) {
AutoEditActionDataSetter editActionData(*this,
EditAction::eRemoveTableRowElement);
nsresult rv = editActionData.CanHandleAndMaybeDispatchBeforeInputEvent();

View File

@@ -10,6 +10,13 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1574596
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script>
function resizingActive(win) {
let Ci = SpecialPowers.Ci;
let editor = SpecialPowers.wrap(win).docShell.editor;
return editor && editor.QueryInterface(Ci.nsIHTMLObjectResizer).isObjectResizingActive;
}
add_task(async function() {
let iframe = document.getElementById("iframe1");
iframe.focus();
@@ -31,15 +38,13 @@ add_task(async function() {
synthesizeMouseAtCenter(target, {}, iframe.contentWindow);
await promiseSelectionChangeEvent;
ok(target.hasAttribute("_moz_resizing"),
"resizers of the <img> should be visible");
ok(resizingActive(iframe.contentWindow), "Resizing should be active");
iframe.style.display = "none";
iframe.offsetHeight; // reflow
await new Promise(SimpleTest.executeSoon);
ok(!target.hasAttribute("_moz_resizing"),
"resizers of the <img> should be hidden");
ok(!resizingActive(iframe.contentWindow), "Resizing should not be active");
iframe.style.display = "";
iframe.offsetHeight; // reflow
@@ -47,9 +52,7 @@ add_task(async function() {
promiseSelectionChangeEvent = waitForSelectionChange();
synthesizeMouseAtCenter(target, {}, iframe.contentWindow);
await promiseSelectionChangeEvent;
ok(target.hasAttribute("_moz_resizing"),
"resizers of the <img> should be visible again");
ok(resizingActive(iframe.contentWindow), "Resizing should be active again");
});
</script>
</head>

View File

@@ -36,9 +36,10 @@ SimpleTest.waitForFocus(async function() {
let promiseSelectionChangeEvent = waitForSelectionChange();
synthesizeMouseAtCenter(img, {});
await promiseSelectionChangeEvent;
ok(img.hasAttribute("_moz_resizing"), "resizers of the <img> should be visible");
getHTMLObjectResizer().hideResizers();
ok(!img.hasAttribute("_moz_resizing"), "resizers of the <img> should be hidden after a call of hideResizers()");
let resizer = getHTMLObjectResizer();
ok(resizer.isObjectResizingActive, "resizers of the <img> should be visible");
resizer.hideResizers();
ok(!resizer.isObjectResizingActive, "resizers of the <img> should be visible");
SimpleTest.finish();
});

View File

@@ -20,6 +20,12 @@
<script class="testbody" type="application/javascript">
"use strict";
function resizingActive(win = window) {
let Ci = SpecialPowers.Ci;
let editor = SpecialPowers.wrap(win).docShell.editor;
return editor && editor.QueryInterface(Ci.nsIHTMLObjectResizer).isObjectResizingActive;
}
SimpleTest.waitForExplicitFinish();
SimpleTest.waitForFocus(async function() {
async function waitForSelectionChange() {
@@ -72,8 +78,7 @@ SimpleTest.waitForFocus(async function() {
let promiseSelectionChangeEvent1 = waitForSelectionChange();
synthesizeMouseAtCenter(target, {});
await promiseSelectionChangeEvent1;
ok(!target.hasAttribute("_moz_resizing"),
ok(!resizingActive(),
kDescription + ": While enableObjectResizing is disabled, resizers shouldn't appear");
document.execCommand("enableObjectResizing", false, true);
@@ -86,16 +91,15 @@ SimpleTest.waitForFocus(async function() {
await promiseSelectionChangeEvent2;
const kResizable = typeof kTest.resizable === "function" ? kTest.resizable() : kTest.resizable;
is(target.hasAttribute("_moz_resizing"), kResizable,
is(resizingActive(), kResizable,
kDescription + (kResizable ? "While enableObjectResizing is enabled, resizers should appear" :
"Even while enableObjectResizing is enabled, resizers shouldn't appear"));
document.execCommand("enableObjectResizing", false, false);
ok(!target.hasAttribute("_moz_resizing"),
ok(!resizingActive(),
kDescription + "enableObjectResizing is disabled even while resizers are visible, resizers should disappear");
document.execCommand("enableObjectResizing", false, true);
is(target.hasAttribute("_moz_resizing"), kResizable,
is(resizingActive(), kResizable,
kDescription + (kResizable ? "enableObjectResizing is enabled when resizable object is selected, resizers should appear" :
"Even if enableObjectResizing is enabled when non-resizable object is selected, resizers shouldn't appear"));
}

View File

@@ -24,6 +24,12 @@
<pre id="test">
<script class="testbody" type="application/javascript">
function resizingActive(win = window) {
let Ci = SpecialPowers.Ci;
let editor = SpecialPowers.wrap(win).docShell.editor;
return editor && editor.QueryInterface(Ci.nsIHTMLObjectResizer).isObjectResizingActive;
}
add_task(async function testAbsPosUI() {
await new Promise((resolve) => {
SimpleTest.waitForFocus(() => {
@@ -73,8 +79,7 @@ add_task(function testResizerUI() {
let img1 = document.getElementById("img1");
synthesizeMouseAtCenter(img1, {});
ok(img1.hasAttribute("_moz_resizing"),
"_moz_resizing attribute should be true");
ok(resizingActive(), "resizing should be active");
synthesizeKey("z", { accelKey: true });
is(edit1.firstChild.textContent, "test", "Text is restored by undo");
@@ -86,8 +91,7 @@ add_task(function testResizerUI() {
isnot(edit1.firstChild.textContent, "test", "Text is modified");
synthesizeMouseAtCenter(img1, {});
ok(img1.hasAttribute("_moz_resizing"),
"_moz_resizing attribute should be true");
ok(resizingActive(), "resizing should be active");
// Emulate drag & drop
let origWidth = img1.width;

View File

@@ -3,274 +3,4 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
@namespace url(http://www.w3.org/1999/xhtml); /* set default namespace to HTML */
/* Scroll-anchoring shouldn't work in any editable and scrollable elements when
user inserts something.
*/
*|*:read-write:focus,
*|*:root:read-write {
overflow-anchor: none;
}
*|*::-moz-canvas {
cursor: text;
}
/* Use default arrow over objects with size that
are selected when clicked on.
Override the browser's pointer cursor over links
*/
img:read-write, img:read-write[usemap], area:read-write,
object:read-write, object:read-write[usemap],
applet:read-write, hr:read-write, button:read-write,
select:read-write,
a:read-write:link img, a:read-write:visited img,
a:read-write:active img, a:read-write:-moz-only-whitespace[name] {
cursor: default;
}
*|*:any-link:read-write {
cursor: text;
}
/* We suppress user/author's prefs for link underline,
so we must set explicitly. This isn't good!
*/
a:link:read-write {
color: -moz-hyperlinktext;
}
option:read-write {
user-select: text;
}
/* the following rules are for Image Resizing */
span[\_moz_anonclass="mozResizer"] {
width: 5px;
height: 5px;
position: absolute;
border: 1px black solid;
background-color: white;
user-select: none;
z-index: 2147483646; /* max value -1 for this property */
}
/* we can't use :active below */
span[\_moz_anonclass="mozResizer"][\_moz_activated],
span[\_moz_anonclass="mozResizer"]:hover {
background-color: black;
}
span[\_moz_anonclass="mozResizer"].hidden,
span[\_moz_anonclass="mozResizingShadow"].hidden,
img[\_moz_anonclass="mozResizingShadow"].hidden,
span[\_moz_anonclass="mozGrabber"].hidden,
span[\_moz_anonclass="mozResizingInfo"].hidden,
a[\_moz_anonclass="mozTableRemoveRow"].hidden,
a[\_moz_anonclass="mozTableRemoveColumn"].hidden {
display: none !important;
}
span[\_moz_anonclass="mozResizer"][anonlocation="nw"] {
cursor: nw-resize;
}
span[\_moz_anonclass="mozResizer"][anonlocation="n"] {
cursor: n-resize;
}
span[\_moz_anonclass="mozResizer"][anonlocation="ne"] {
cursor: ne-resize;
}
span[\_moz_anonclass="mozResizer"][anonlocation="w"] {
cursor: w-resize;
}
span[\_moz_anonclass="mozResizer"][anonlocation="e"] {
cursor: e-resize;
}
span[\_moz_anonclass="mozResizer"][anonlocation="sw"] {
cursor: sw-resize;
}
span[\_moz_anonclass="mozResizer"][anonlocation="s"] {
cursor: s-resize;
}
span[\_moz_anonclass="mozResizer"][anonlocation="se"] {
cursor: se-resize;
}
span[\_moz_anonclass="mozResizingShadow"],
img[\_moz_anonclass="mozResizingShadow"] {
outline: thin dashed black;
user-select: none;
opacity: 0.5;
position: absolute;
z-index: 2147483647; /* max value for this property */
}
span[\_moz_anonclass="mozResizingInfo"] {
font-family: sans-serif;
font-size: x-small;
color: black;
background-color: #d0d0d0;
border: ridge 2px #d0d0d0;
padding: 2px;
position: absolute;
z-index: 2147483647; /* max value for this property */
}
img[\_moz_resizing] {
outline: thin solid black;
}
*[\_moz_abspos] {
outline: silver ridge 2px;
z-index: 2147483645 !important; /* max value -2 for this property */
}
*[\_moz_abspos="white"] {
background-color: white !important;
}
*[\_moz_abspos="black"] {
background-color: black !important;
}
span[\_moz_anonclass="mozGrabber"] {
outline: ridge 2px silver;
padding: 2px;
position: absolute;
width: 12px;
height: 12px;
background-image: url("resource://gre/res/grabber.gif");
background-repeat: no-repeat;
background-position: center center;
user-select: none;
cursor: move;
}
/* INLINE TABLE EDITING */
a[\_moz_anonclass="mozTableAddColumnBefore"] {
position: absolute;
z-index: 2147483647; /* max value for this property */
text-decoration: none !important;
border: none 0 !important;
width: 4px;
height: 8px;
background-image: url("resource://gre/res/table-add-column-before.gif");
background-repeat: no-repeat;
background-position: center center;
user-select: none;
}
a[\_moz_anonclass="mozTableAddColumnBefore"]:hover {
background-image: url("resource://gre/res/table-add-column-before-hover.gif");
}
a[\_moz_anonclass="mozTableAddColumnBefore"]:active {
background-image: url("resource://gre/res/table-add-column-before-active.gif");
}
a[\_moz_anonclass="mozTableAddColumnAfter"] {
position: absolute;
z-index: 2147483647; /* max value for this property */
text-decoration: none !important;
border: none 0 !important;
width: 4px;
height: 8px;
background-image: url("resource://gre/res/table-add-column-after.gif");
background-repeat: no-repeat;
background-position: center center;
user-select: none;
}
a[\_moz_anonclass="mozTableAddColumnAfter"]:hover {
background-image: url("resource://gre/res/table-add-column-after-hover.gif");
}
a[\_moz_anonclass="mozTableAddColumnAfter"]:active {
background-image: url("resource://gre/res/table-add-column-after-active.gif");
}
a[\_moz_anonclass="mozTableRemoveColumn"] {
position: absolute;
z-index: 2147483647; /* max value for this property */
text-decoration: none !important;
border: none 0 !important;
width: 8px;
height: 8px;
background-image: url("resource://gre/res/table-remove-column.gif");
background-repeat: no-repeat;
background-position: center center;
user-select: none;
}
a[\_moz_anonclass="mozTableRemoveColumn"]:hover {
background-image: url("resource://gre/res/table-remove-column-hover.gif");
}
a[\_moz_anonclass="mozTableRemoveColumn"]:active {
background-image: url("resource://gre/res/table-remove-column-active.gif");
}
a[\_moz_anonclass="mozTableAddRowBefore"] {
position: absolute;
z-index: 2147483647; /* max value for this property */
text-decoration: none !important;
border: none 0 !important;
width: 8px;
height: 4px;
background-image: url("resource://gre/res/table-add-row-before.gif");
background-repeat: no-repeat;
background-position: center center;
user-select: none;
}
a[\_moz_anonclass="mozTableAddRowBefore"]:hover {
background-image: url("resource://gre/res/table-add-row-before-hover.gif");
}
a[\_moz_anonclass="mozTableAddRowBefore"]:active {
background-image: url("resource://gre/res/table-add-row-before-active.gif");
}
a[\_moz_anonclass="mozTableAddRowAfter"] {
position: absolute;
z-index: 2147483647; /* max value for this property */
text-decoration: none !important;
border: none 0 !important;
width: 8px;
height: 4px;
background-image: url("resource://gre/res/table-add-row-after.gif");
background-repeat: no-repeat;
background-position: center center;
user-select: none;
}
a[\_moz_anonclass="mozTableAddRowAfter"]:hover {
background-image: url("resource://gre/res/table-add-row-after-hover.gif");
}
a[\_moz_anonclass="mozTableAddRowAfter"]:active {
background-image: url("resource://gre/res/table-add-row-after-active.gif");
}
a[\_moz_anonclass="mozTableRemoveRow"] {
position: absolute;
z-index: 2147483647; /* max value for this property */
text-decoration: none !important;
border: none 0 !important;
width: 8px;
height: 8px;
background-image: url("resource://gre/res/table-remove-row.gif");
background-repeat: no-repeat;
background-position: center center;
user-select: none;
}
a[\_moz_anonclass="mozTableRemoveRow"]:hover {
background-image: url("resource://gre/res/table-remove-row-hover.gif");
}
a[\_moz_anonclass="mozTableRemoveRow"]:active {
background-image: url("resource://gre/res/table-remove-row-active.gif");
}
/* contenteditable.css is empty, see bug 1941134 */

View File

@@ -2,4 +2,4 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* Empty, see bug 1941134 */
/* designmode.css is empty, see bug 1941134 */

View File

@@ -312,6 +312,10 @@ option {
word-wrap: normal !important;
}
option:read-write {
user-select: text;
}
select > option {
padding-inline: 4px;
}

View File

@@ -921,3 +921,142 @@ dialog:popover-open {
pointer-events: none !important;
background-color: transparent;
}
/* Editor support */
.mozResizer:-moz-native-anonymous {
width: 5px;
height: 5px;
position: absolute;
border: 1px black solid;
background-color: white;
user-select: none;
z-index: 2147483646; /* max value -1 for this property */
/* we can't use :active below */
&.active,
&:hover {
background-color: black;
}
}
.mozGrabber:-moz-native-anonymous {
outline: ridge 2px silver;
padding: 2px;
position: absolute;
width: 12px;
height: 12px;
background-image: url("resource://gre/res/grabber.gif");
background-repeat: no-repeat;
background-position: center center;
user-select: none;
cursor: move;
}
.mozResizingShadow:-moz-native-anonymous {
outline: thin dashed black;
user-select: none;
opacity: 0.5;
position: absolute;
z-index: 2147483647; /* max value for this property */
}
.mozResizingInfo:-moz-native-anonymous {
font-family: sans-serif;
font-size: x-small;
color: black;
background-color: #d0d0d0;
border: ridge 2px #d0d0d0;
padding: 2px;
position: absolute;
z-index: 2147483647; /* max value for this property */
}
.mozTableAddColumnBefore:-moz-native-anonymous,
.mozTableAddColumnAfter:-moz-native-anonymous,
.mozTableAddRowBefore:-moz-native-anonymous,
.mozTableAddRowAfter:-moz-native-anonymous,
.mozTableRemoveColumn:-moz-native-anonymous,
.mozTableRemoveRow:-moz-native-anonymous {
position: absolute;
z-index: 2147483647; /* max value for this property */
text-decoration: none !important;
border: none 0 !important;
width: 4px;
height: 8px;
background-repeat: no-repeat;
background-position: center center;
user-select: none;
}
.mozTableAddColumnBefore:-moz-native-anonymous {
background-image: url("resource://gre/res/table-add-column-before.gif");
&:hover {
background-image: url("resource://gre/res/table-add-column-before-hover.gif");
}
&:active {
background-image: url("resource://gre/res/table-add-column-before-active.gif");
}
}
.mozTableAddColumnAfter:-moz-native-anonymous {
background-image: url("resource://gre/res/table-add-column-after.gif");
&:hover {
background-image: url("resource://gre/res/table-add-column-after-hover.gif");
}
&:active {
background-image: url("resource://gre/res/table-add-column-after-active.gif");
}
}
.mozTableAddRowBefore:-moz-native-anonymous,
.mozTableAddRowAfter:-moz-native-anonymous {
width: 8px;
height: 4px;
}
.mozTableAddRowBefore:-moz-native-anonymous {
background-image: url("resource://gre/res/table-add-row-before.gif");
&:hover {
background-image: url("resource://gre/res/table-add-row-before-hover.gif");
}
&:active {
background-image: url("resource://gre/res/table-add-row-before-active.gif");
}
}
.mozTableAddRowAfter:-moz-native-anonymous {
background-image: url("resource://gre/res/table-add-row-after.gif");
&:hover {
background-image: url("resource://gre/res/table-add-row-after-hover.gif");
}
&:active {
background-image: url("resource://gre/res/table-add-row-after-active.gif");
}
}
.mozTableRemoveColumn:-moz-native-anonymous,
.mozTableRemoveRow:-moz-native-anonymous {
width: 8px;
height: 8px;
}
.mozTableRemoveColumn:-moz-native-anonymous {
background-image: url("resource://gre/res/table-remove-column.gif");
&:hover {
background-image: url("resource://gre/res/table-remove-column-hover.gif");
}
&:active {
background-image: url("resource://gre/res/table-remove-column-active.gif");
}
}
.mozTableRemoveRow:-moz-native-anonymous {
background-image: url("resource://gre/res/table-remove-row.gif");
&:hover {
background-image: url("resource://gre/res/table-remove-row-hover.gif");
}
&:active {
background-image: url("resource://gre/res/table-remove-row-active.gif");
}
}

View File

@@ -157,12 +157,16 @@
color: LinkText;
}
:visited {
color: VisitedText;
}
:any-link:active {
color: ActiveText;
}
:visited {
color: VisitedText;
:any-link:read-write {
cursor: text;
}
/* stylelint-disable-next-line media-query-no-invalid */
@@ -453,3 +457,12 @@ parsererror|sourcetext {
/* Initial direction depends on the document, make sure to reset it */
direction: ltr;
}
/* contenteditable support */
:read-write:focus,
:root:read-write {
/* Scroll-anchoring shouldn't work in any editable and scrollable elements
* when user inserts something. */
overflow-anchor: none;
}