gecko-dev/editor/libeditor/HTMLAbsPositionEditor.cpp
Tiaan Louw c8476cb106 Bug 1817641 - Convert RGBA to AbsoluteColor for computed/animated/resolved CSS colors r=emilio
Computed color values will not be in the correct format, closer to the
one specified by the author.  This also means that colors accross the
code are stored now as AbsoluteColor or StyleAbsoluteColor.  This allows
color space/gamut information to be available for use.

Some animation related test failures had to be changed, because colors
now has greater precision.  Animated a color now causes a lot more
animation updates, which was not initially expected.  See the bug for
discussion.

Differential Revision: https://phabricator.services.mozilla.com/D171021
2023-03-07 11:28:15 +00:00

994 lines
34 KiB
C++

/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
#include "HTMLEditor.h"
#include <math.h>
#include "CSSEditUtils.h"
#include "EditAction.h"
#include "HTMLEditHelpers.h"
#include "HTMLEditorEventListener.h"
#include "HTMLEditUtils.h"
#include "mozilla/EventListenerManager.h"
#include "mozilla/mozalloc.h"
#include "mozilla/Preferences.h"
#include "mozilla/PresShell.h"
#include "mozilla/StaticPrefs_editor.h"
#include "mozilla/dom/AncestorIterator.h"
#include "mozilla/dom/Selection.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/EventTarget.h"
#include "nsAString.h"
#include "nsAlgorithm.h"
#include "nsCOMPtr.h"
#include "nsComputedDOMStyle.h"
#include "nsDebug.h"
#include "nsError.h"
#include "nsGkAtoms.h"
#include "nsIContent.h"
#include "nsROCSSPrimitiveValue.h"
#include "nsINode.h"
#include "nsIPrincipal.h"
#include "nsISupportsImpl.h"
#include "nsISupportsUtils.h"
#include "nsLiteralString.h"
#include "nsReadableUtils.h"
#include "nsString.h"
#include "nsStringFwd.h"
#include "nsStyledElement.h"
#include "nscore.h"
#include <algorithm>
namespace mozilla {
using namespace dom;
nsresult HTMLEditor::SetSelectionToAbsoluteOrStaticAsAction(
bool aEnabled, nsIPrincipal* aPrincipal) {
AutoEditActionDataSetter editActionData(
*this, EditAction::eSetPositionToAbsoluteOrStatic, aPrincipal);
nsresult rv = editActionData.CanHandleAndMaybeDispatchBeforeInputEvent();
if (NS_FAILED(rv)) {
NS_WARNING_ASSERTION(rv == NS_ERROR_EDITOR_ACTION_CANCELED,
"CanHandleAndMaybeDispatchBeforeInputEvent(), failed");
return rv;
}
const RefPtr<Element> editingHost = ComputeEditingHost();
if (!editingHost) {
return NS_SUCCESS_DOM_NO_OPERATION;
}
if (aEnabled) {
Result<EditActionResult, nsresult> result =
SetSelectionToAbsoluteAsSubAction(*editingHost);
if (MOZ_UNLIKELY(result.isErr())) {
NS_WARNING("HTMLEditor::SetSelectionToAbsoluteAsSubAction() failed");
return result.unwrapErr();
}
return NS_OK;
}
Result<EditActionResult, nsresult> result = SetSelectionToStaticAsSubAction();
if (MOZ_UNLIKELY(result.isErr())) {
NS_WARNING("HTMLEditor::SetSelectionToStaticAsSubAction() failed");
return result.unwrapErr();
}
return NS_OK;
}
already_AddRefed<Element>
HTMLEditor::GetAbsolutelyPositionedSelectionContainer() const {
AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing);
if (NS_WARN_IF(!editActionData.CanHandle())) {
return nullptr;
}
Element* selectionContainerElement = GetSelectionContainerElement();
if (NS_WARN_IF(!selectionContainerElement)) {
return nullptr;
}
AutoTArray<RefPtr<Element>, 24> arrayOfParentElements;
for (Element* element :
selectionContainerElement->InclusiveAncestorsOfType<Element>()) {
arrayOfParentElements.AppendElement(element);
}
nsAutoString positionValue;
for (RefPtr<Element> element = selectionContainerElement; element;
element = element->GetParentElement()) {
if (element->IsHTMLElement(nsGkAtoms::html)) {
NS_WARNING(
"HTMLEditor::GetAbsolutelyPositionedSelectionContainer() reached "
"<html> element");
return nullptr;
}
nsCOMPtr<nsINode> parentNode = element->GetParentNode();
nsresult rv = CSSEditUtils::GetComputedProperty(
MOZ_KnownLive(*element), *nsGkAtoms::position, positionValue);
if (NS_FAILED(rv)) {
NS_WARNING(
"CSSEditUtils::GetComputedProperty(nsGkAtoms::position) failed");
return nullptr;
}
if (NS_WARN_IF(Destroyed()) ||
NS_WARN_IF(parentNode != element->GetParentNode())) {
return nullptr;
}
if (positionValue.EqualsLiteral("absolute")) {
return element.forget();
}
}
return nullptr;
}
NS_IMETHODIMP HTMLEditor::GetAbsolutePositioningEnabled(bool* aIsEnabled) {
*aIsEnabled = IsAbsolutePositionEditorEnabled();
return NS_OK;
}
NS_IMETHODIMP HTMLEditor::SetAbsolutePositioningEnabled(bool aIsEnabled) {
EnableAbsolutePositionEditor(aIsEnabled);
return NS_OK;
}
Result<int32_t, nsresult> HTMLEditor::AddZIndexWithTransaction(
nsStyledElement& aStyledElement, int32_t aChange) {
if (!aChange) {
return 0; // XXX Why don't we return current z-index value in this case?
}
int32_t zIndex = GetZIndex(aStyledElement);
if (NS_WARN_IF(Destroyed())) {
return Err(NS_ERROR_EDITOR_DESTROYED);
}
zIndex = std::max(zIndex + aChange, 0);
nsresult rv = SetZIndexWithTransaction(aStyledElement, zIndex);
if (rv == NS_ERROR_EDITOR_DESTROYED) {
NS_WARNING("HTMLEditor::SetZIndexWithTransaction() destroyed the editor");
return Err(NS_ERROR_EDITOR_DESTROYED);
}
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rv),
"HTMLEditor::SetZIndexWithTransaction() failed, but ignored");
return zIndex;
}
nsresult HTMLEditor::SetZIndexWithTransaction(nsStyledElement& aStyledElement,
int32_t aZIndex) {
nsAutoString zIndexValue;
zIndexValue.AppendInt(aZIndex);
nsresult rv = CSSEditUtils::SetCSSPropertyWithTransaction(
*this, aStyledElement, *nsGkAtoms::z_index, zIndexValue);
if (rv == NS_ERROR_EDITOR_DESTROYED) {
NS_WARNING(
"CSSEditUtils::SetCSSPropertyWithTransaction(nsGkAtoms::z_index) "
"destroyed the editor");
return NS_ERROR_EDITOR_DESTROYED;
}
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"CSSEditUtils::SetCSSPropertyWithTransaction(nsGkAtoms::"
"z_index) failed, but ignored");
return NS_OK;
}
nsresult HTMLEditor::AddZIndexAsAction(int32_t aChange,
nsIPrincipal* aPrincipal) {
MOZ_ASSERT(IsEditActionDataAvailable());
AutoEditActionDataSetter editActionData(
*this, EditAction::eIncreaseOrDecreaseZIndex, aPrincipal);
nsresult rv = editActionData.CanHandleAndMaybeDispatchBeforeInputEvent();
if (NS_FAILED(rv)) {
NS_WARNING_ASSERTION(rv == NS_ERROR_EDITOR_ACTION_CANCELED,
"CanHandleAndMaybeDispatchBeforeInputEvent(), failed");
return EditorBase::ToGenericNSResult(rv);
}
Result<EditActionResult, nsresult> result = AddZIndexAsSubAction(aChange);
if (MOZ_UNLIKELY(result.isErr())) {
NS_WARNING("HTMLEditor::AddZIndexAsSubAction() failed");
return EditorBase::ToGenericNSResult(result.unwrapErr());
}
return NS_OK;
}
int32_t HTMLEditor::GetZIndex(Element& aElement) {
AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing);
if (NS_WARN_IF(!editActionData.CanHandle())) {
return 0;
}
nsAutoString zIndexValue;
nsresult rv = CSSEditUtils::GetSpecifiedProperty(
aElement, *nsGkAtoms::z_index, zIndexValue);
if (NS_FAILED(rv)) {
NS_WARNING("CSSEditUtils::GetSpecifiedProperty(nsGkAtoms::z_index) failed");
return 0;
}
if (zIndexValue.EqualsLiteral("auto")) {
if (!aElement.GetParentElement()) {
NS_WARNING("aElement was an orphan node or the root node");
return 0;
}
// we have to look at the positioned ancestors
// cf. CSS 2 spec section 9.9.1
nsAutoString positionValue;
for (RefPtr<Element> element = aElement.GetParentElement(); element;
element = element->GetParentElement()) {
if (element->IsHTMLElement(nsGkAtoms::body)) {
return 0;
}
nsCOMPtr<nsINode> parentNode = element->GetParentElement();
nsresult rv = CSSEditUtils::GetComputedProperty(
*element, *nsGkAtoms::position, positionValue);
if (NS_FAILED(rv)) {
NS_WARNING(
"CSSEditUtils::GetComputedProperty(nsGkAtoms::position) failed");
return 0;
}
if (NS_WARN_IF(Destroyed()) ||
NS_WARN_IF(parentNode != element->GetParentNode())) {
return 0;
}
if (!positionValue.EqualsLiteral("absolute")) {
continue;
}
// ah, we found one, what's its z-index ? If its z-index is auto,
// we have to continue climbing the document's tree
rv = CSSEditUtils::GetComputedProperty(*element, *nsGkAtoms::z_index,
zIndexValue);
if (NS_FAILED(rv)) {
NS_WARNING(
"CSSEditUtils::GetComputedProperty(nsGkAtoms::z_index) failed");
return 0;
}
if (NS_WARN_IF(Destroyed()) ||
NS_WARN_IF(parentNode != element->GetParentNode())) {
return 0;
}
if (!zIndexValue.EqualsLiteral("auto")) {
break;
}
}
}
if (zIndexValue.EqualsLiteral("auto")) {
return 0;
}
nsresult rvIgnored;
int32_t result = zIndexValue.ToInteger(&rvIgnored);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
"nsAString::ToInteger() failed, but ignored");
return result;
}
bool HTMLEditor::CreateGrabberInternal(nsIContent& aParentContent) {
if (NS_WARN_IF(mGrabber)) {
return false;
}
mGrabber = CreateAnonymousElement(nsGkAtoms::span, aParentContent,
u"mozGrabber"_ns, false);
// mGrabber may be destroyed during creation due to there may be
// mutation event listener.
if (!mGrabber) {
NS_WARNING(
"HTMLEditor::CreateAnonymousElement(nsGkAtoms::span, mozGrabber) "
"failed");
return false;
}
EventListenerManager* eventListenerManager =
mGrabber->GetOrCreateListenerManager();
eventListenerManager->AddEventListenerByType(
mEventListener, u"mousedown"_ns, TrustedEventsAtSystemGroupBubble());
MOZ_ASSERT(mGrabber);
return true;
}
nsresult HTMLEditor::RefreshGrabberInternal() {
MOZ_ASSERT(IsEditActionDataAvailable());
if (!mAbsolutelyPositionedObject) {
return NS_OK;
}
OwningNonNull<Element> absolutelyPositionedObject =
*mAbsolutelyPositionedObject;
nsresult rv = GetPositionAndDimensions(
absolutelyPositionedObject, mPositionedObjectX, mPositionedObjectY,
mPositionedObjectWidth, mPositionedObjectHeight,
mPositionedObjectBorderLeft, mPositionedObjectBorderTop,
mPositionedObjectMarginLeft, mPositionedObjectMarginTop);
if (NS_FAILED(rv)) {
NS_WARNING("HTMLEditor::GetPositionAndDimensions() failed");
return rv;
}
if (NS_WARN_IF(absolutelyPositionedObject != mAbsolutelyPositionedObject)) {
return NS_ERROR_FAILURE;
}
RefPtr<nsStyledElement> grabberStyledElement =
nsStyledElement::FromNodeOrNull(mGrabber.get());
if (!grabberStyledElement) {
return NS_OK;
}
rv = SetAnonymousElementPositionWithoutTransaction(
*grabberStyledElement, mPositionedObjectX + 12, mPositionedObjectY - 14);
if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}
if (NS_FAILED(rv)) {
NS_WARNING(
"HTMLEditor::SetAnonymousElementPositionWithoutTransaction() failed");
return rv;
}
if (NS_WARN_IF(grabberStyledElement != mGrabber.get())) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
void HTMLEditor::HideGrabberInternal() {
if (NS_WARN_IF(!mAbsolutelyPositionedObject)) {
return;
}
// Move all members to the local variables first since mutation event
// listener may try to show grabber while we're hiding them.
RefPtr<Element> absolutePositioningObject =
std::move(mAbsolutelyPositionedObject);
ManualNACPtr grabber = std::move(mGrabber);
ManualNACPtr positioningShadow = std::move(mPositioningShadow);
// If we're still in dragging mode, it means that the dragging is canceled
// by the web app.
if (mGrabberClicked || mIsMoving) {
mGrabberClicked = false;
mIsMoving = false;
if (mEventListener) {
DebugOnly<nsresult> rvIgnored =
static_cast<HTMLEditorEventListener*>(mEventListener.get())
->ListenToMouseMoveEventForGrabber(false);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
"HTMLEditorEventListener::"
"ListenToMouseMoveEventForGrabber(false) failed");
}
}
DebugOnly<nsresult> rv = absolutePositioningObject->UnsetAttr(
kNameSpaceID_None, nsGkAtoms::_moz_abspos, true);
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rv),
"Element::UnsetAttr(nsGkAtoms::_moz_abspos) failed, but ignored");
// We allow the pres shell to be null; when it is, we presume there
// are no document observers to notify, but we still want to
// UnbindFromTree.
RefPtr<PresShell> presShell = GetPresShell();
if (grabber) {
DeleteRefToAnonymousNode(std::move(grabber), presShell);
}
if (positioningShadow) {
DeleteRefToAnonymousNode(std::move(positioningShadow), presShell);
}
}
nsresult HTMLEditor::ShowGrabberInternal(Element& aElement) {
MOZ_ASSERT(IsEditActionDataAvailable());
const RefPtr<Element> editingHost = ComputeEditingHost();
if (NS_WARN_IF(!editingHost) ||
NS_WARN_IF(!aElement.IsInclusiveDescendantOf(editingHost))) {
return NS_ERROR_UNEXPECTED;
}
if (NS_WARN_IF(mGrabber)) {
return NS_ERROR_UNEXPECTED;
}
nsAutoString classValue;
nsresult rv =
GetTemporaryStyleForFocusedPositionedElement(aElement, classValue);
if (NS_FAILED(rv)) {
NS_WARNING(
"HTMLEditor::GetTemporaryStyleForFocusedPositionedElement() failed");
return rv;
}
rv = aElement.SetAttr(kNameSpaceID_None, nsGkAtoms::_moz_abspos, classValue,
true);
if (NS_FAILED(rv)) {
NS_WARNING("Element::SetAttr(nsGkAtoms::_moz_abspos) failed");
return rv;
}
mAbsolutelyPositionedObject = &aElement;
Element* parentElement = aElement.GetParentElement();
if (NS_WARN_IF(!parentElement)) {
return NS_ERROR_FAILURE;
}
if (!CreateGrabberInternal(*parentElement)) {
NS_WARNING("HTMLEditor::CreateGrabberInternal() failed");
return NS_ERROR_FAILURE;
}
// If we succeeded to create the grabber, HideGrabberInternal() hasn't been
// called yet. So, mAbsolutelyPositionedObject should be non-nullptr.
MOZ_ASSERT(mAbsolutelyPositionedObject);
// Finally, move the grabber to proper position.
rv = RefreshGrabberInternal();
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"HTMLEditor::RefereshGrabberInternal() failed");
return rv;
}
nsresult HTMLEditor::StartMoving() {
MOZ_ASSERT(mGrabber);
RefPtr<Element> parentElement = mGrabber->GetParentElement();
if (NS_WARN_IF(!parentElement) || NS_WARN_IF(!mAbsolutelyPositionedObject)) {
return NS_ERROR_FAILURE;
}
// now, let's create the resizing shadow
mPositioningShadow =
CreateShadow(*parentElement, *mAbsolutelyPositionedObject);
if (!mPositioningShadow) {
NS_WARNING("HTMLEditor::CreateShadow() failed");
return NS_ERROR_FAILURE;
}
if (!mAbsolutelyPositionedObject) {
NS_WARNING("The target has gone during HTMLEditor::CreateShadow()");
return NS_ERROR_FAILURE;
}
RefPtr<Element> positioningShadow = mPositioningShadow.get();
RefPtr<Element> absolutelyPositionedObject = mAbsolutelyPositionedObject;
nsresult rv =
SetShadowPosition(*positioningShadow, *absolutelyPositionedObject,
mPositionedObjectX, mPositionedObjectY);
if (NS_FAILED(rv)) {
NS_WARNING("HTMLEditor::SetShadowPosition() failed");
return rv;
}
// make the shadow appear
DebugOnly<nsresult> rvIgnored =
mPositioningShadow->UnsetAttr(kNameSpaceID_None, nsGkAtoms::_class, true);
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rvIgnored),
"Element::UnsetAttr(nsGkAtoms::_class) failed, but ignored");
// position it
if (RefPtr<nsStyledElement> positioningShadowStyledElement =
nsStyledElement::FromNode(mPositioningShadow.get())) {
nsresult rv;
rv = CSSEditUtils::SetCSSPropertyPixelsWithoutTransaction(
*positioningShadowStyledElement, *nsGkAtoms::width,
mPositionedObjectWidth);
if (rv == NS_ERROR_EDITOR_DESTROYED) {
NS_WARNING(
"CSSEditUtils::SetCSSPropertyPixelsWithoutTransaction("
"nsGkAtoms::width) destroyed the editor");
return NS_ERROR_EDITOR_DESTROYED;
}
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"CSSEditUtils::SetCSSPropertyPixelsWithoutTransaction("
"nsGkAtoms::width) failed, but ignored");
rv = CSSEditUtils::SetCSSPropertyPixelsWithoutTransaction(
*positioningShadowStyledElement, *nsGkAtoms::height,
mPositionedObjectHeight);
if (rv == NS_ERROR_EDITOR_DESTROYED) {
NS_WARNING(
"CSSEditUtils::SetCSSPropertyPixelsWithoutTransaction("
"nsGkAtoms::height) destroyed the editor");
return NS_ERROR_EDITOR_DESTROYED;
}
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"CSSEditUtils::SetCSSPropertyPixelsWithoutTransaction("
"nsGkAtoms::height) failed, but ignored");
}
mIsMoving = true;
return NS_OK; // XXX Looks like nobody refers this result
}
void HTMLEditor::SnapToGrid(int32_t& newX, int32_t& newY) const {
if (mSnapToGridEnabled && mGridSize) {
newX = (int32_t)floor(((float)newX / (float)mGridSize) + 0.5f) * mGridSize;
newY = (int32_t)floor(((float)newY / (float)mGridSize) + 0.5f) * mGridSize;
}
}
nsresult HTMLEditor::GrabberClicked() {
if (NS_WARN_IF(!mEventListener)) {
return NS_ERROR_NOT_INITIALIZED;
}
nsresult rv = static_cast<HTMLEditorEventListener*>(mEventListener.get())
->ListenToMouseMoveEventForGrabber(true);
if (NS_FAILED(rv)) {
NS_WARNING(
"HTMLEditorEventListener::ListenToMouseMoveEventForGrabber(true) "
"failed, but ignored");
return NS_OK;
}
mGrabberClicked = true;
return NS_OK;
}
nsresult HTMLEditor::EndMoving() {
if (mPositioningShadow) {
RefPtr<PresShell> presShell = GetPresShell();
if (NS_WARN_IF(!presShell)) {
return NS_ERROR_NOT_INITIALIZED;
}
DeleteRefToAnonymousNode(std::move(mPositioningShadow), presShell);
mPositioningShadow = nullptr;
}
if (mEventListener) {
DebugOnly<nsresult> rvIgnored =
static_cast<HTMLEditorEventListener*>(mEventListener.get())
->ListenToMouseMoveEventForGrabber(false);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
"HTMLEditorEventListener::"
"ListenToMouseMoveEventForGrabber(false) failed");
}
mGrabberClicked = false;
mIsMoving = false;
nsresult rv = RefreshEditingUI();
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"HTMLEditor::RefreshEditingUI() failed");
return rv;
}
nsresult HTMLEditor::SetFinalPosition(int32_t aX, int32_t aY) {
MOZ_ASSERT(IsEditActionDataAvailable());
nsresult rv = EndMoving();
if (NS_FAILED(rv)) {
NS_WARNING("HTMLEditor::EndMoving() failed");
return rv;
}
// we have now to set the new width and height of the resized object
// we don't set the x and y position because we don't control that in
// a normal HTML layout
int32_t newX = mPositionedObjectX + aX - mOriginalX -
(mPositionedObjectBorderLeft + mPositionedObjectMarginLeft);
int32_t newY = mPositionedObjectY + aY - mOriginalY -
(mPositionedObjectBorderTop + mPositionedObjectMarginTop);
SnapToGrid(newX, newY);
nsAutoString x, y;
x.AppendInt(newX);
y.AppendInt(newY);
// we want one transaction only from a user's point of view
AutoPlaceholderBatch treatAsOneTransaction(
*this, ScrollSelectionIntoView::Yes, __FUNCTION__);
if (NS_WARN_IF(!mAbsolutelyPositionedObject)) {
return NS_ERROR_FAILURE;
}
if (RefPtr<nsStyledElement> styledAbsolutelyPositionedElement =
nsStyledElement::FromNode(mAbsolutelyPositionedObject)) {
nsresult rv;
rv = CSSEditUtils::SetCSSPropertyPixelsWithTransaction(
*this, *styledAbsolutelyPositionedElement, *nsGkAtoms::top, newY);
if (rv == NS_ERROR_EDITOR_DESTROYED) {
NS_WARNING(
"CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::top) "
"destroyed the editor");
return NS_ERROR_EDITOR_DESTROYED;
}
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rv),
"CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::top) "
"failed, but ignored");
rv = CSSEditUtils::SetCSSPropertyPixelsWithTransaction(
*this, *styledAbsolutelyPositionedElement, *nsGkAtoms::left, newX);
if (rv == NS_ERROR_EDITOR_DESTROYED) {
NS_WARNING(
"CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::left) "
"destroyed the editor");
return NS_ERROR_EDITOR_DESTROYED;
}
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rv),
"CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::left) "
"failed, but ignored");
}
// keep track of that size
mPositionedObjectX = newX;
mPositionedObjectY = newY;
rv = RefreshResizersInternal();
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"HTMLEditor::RefreshResizersInternal() failed");
return rv;
}
nsresult HTMLEditor::SetPositionToAbsoluteOrStatic(Element& aElement,
bool aEnabled) {
nsAutoString positionValue;
DebugOnly<nsresult> rvIgnored = CSSEditUtils::GetComputedProperty(
aElement, *nsGkAtoms::position, positionValue);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
"CSSEditUtils::GetComputedProperty(nsGkAtoms::position) "
"failed, but ignored");
// nothing to do if the element is already in the state we want
if (positionValue.EqualsLiteral("absolute") == aEnabled) {
return NS_OK;
}
if (aEnabled) {
nsresult rv = SetPositionToAbsolute(aElement);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"HTMLEditor::SetPositionToAbsolute() failed");
return rv;
}
nsresult rv = SetPositionToStatic(aElement);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"HTMLEditor::SetPositionToStatic() failed");
return rv;
}
nsresult HTMLEditor::SetPositionToAbsolute(Element& aElement) {
MOZ_ASSERT(IsEditActionDataAvailable());
AutoPlaceholderBatch treatAsOneTransaction(
*this, ScrollSelectionIntoView::Yes, __FUNCTION__);
int32_t x, y;
DebugOnly<nsresult> rvIgnored = GetElementOrigin(aElement, x, y);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
"HTMLEditor::GetElementOrigin() failed, but ignored");
nsStyledElement* styledElement = nsStyledElement::FromNode(&aElement);
if (styledElement) {
// MOZ_KnownLive(*styledElement): aElement's lifetime must be guarantted
// by the caller because of MOZ_CAN_RUN_SCRIPT method.
nsresult rv = CSSEditUtils::SetCSSPropertyWithTransaction(
*this, MOZ_KnownLive(*styledElement), *nsGkAtoms::position,
u"absolute"_ns);
if (rv == NS_ERROR_EDITOR_DESTROYED) {
NS_WARNING(
"CSSEditUtils::SetCSSProperyWithTransaction(nsGkAtoms::Position) "
"destroyed the editor");
return NS_ERROR_EDITOR_DESTROYED;
}
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rvIgnored),
"CSSEditUtils::SetCSSPropertyWithTransaction(nsGkAtoms::position, "
"absolute) failed, but ignored");
}
SnapToGrid(x, y);
if (styledElement) {
// MOZ_KnownLive(*styledElement): aElement's lifetime must be guarantted
// by the caller because of MOZ_CAN_RUN_SCRIPT method.
nsresult rv =
SetTopAndLeftWithTransaction(MOZ_KnownLive(*styledElement), x, y);
if (NS_FAILED(rv)) {
NS_WARNING("HTMLEditor::SetTopAndLeftWithTransaction() failed");
return rv;
}
}
// we may need to create a br if the positioned element is alone in its
// container
nsINode* parentNode = aElement.GetParentNode();
if (parentNode->GetChildCount() != 1) {
return NS_OK;
}
Result<CreateElementResult, nsresult> insertBRElementResult =
InsertBRElement(WithTransaction::Yes, EditorDOMPoint(parentNode, 0u));
if (MOZ_UNLIKELY(insertBRElementResult.isErr())) {
NS_WARNING("HTMLEditor::InsertBRElement(WithTransaction::Yes) failed");
return insertBRElementResult.unwrapErr();
}
// XXX Is this intentional selection change?
nsresult rv = insertBRElementResult.inspect().SuggestCaretPointTo(
*this, {SuggestCaret::OnlyIfHasSuggestion,
SuggestCaret::OnlyIfTransactionsAllowedToDoIt});
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"CreateElementResult::SuggestCaretPointTo() failed");
MOZ_ASSERT(insertBRElementResult.inspect().GetNewNode());
return rv;
}
nsresult HTMLEditor::SetPositionToStatic(Element& aElement) {
nsStyledElement* styledElement = nsStyledElement::FromNode(&aElement);
if (NS_WARN_IF(!styledElement)) {
return NS_ERROR_INVALID_ARG;
}
AutoPlaceholderBatch treatAsOneTransaction(
*this, ScrollSelectionIntoView::Yes, __FUNCTION__);
nsresult rv;
// MOZ_KnownLive(*styledElement): aElement's lifetime must be guarantted
// by the caller because of MOZ_CAN_RUN_SCRIPT method.
rv = CSSEditUtils::RemoveCSSPropertyWithTransaction(
*this, MOZ_KnownLive(*styledElement), *nsGkAtoms::position, u""_ns);
if (rv == NS_ERROR_EDITOR_DESTROYED) {
NS_WARNING(
"CSSEditUtils::RemoveCSSPropertyWithTransaction(nsGkAtoms::position) "
"destroyed the editor");
return NS_ERROR_EDITOR_DESTROYED;
}
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rv),
"CSSEditUtils::RemoveCSSPropertyWithTransaction(nsGkAtoms::position) "
"failed, but ignored");
// MOZ_KnownLive(*styledElement): aElement's lifetime must be guarantted
// by the caller because of MOZ_CAN_RUN_SCRIPT method.
rv = CSSEditUtils::RemoveCSSPropertyWithTransaction(
*this, MOZ_KnownLive(*styledElement), *nsGkAtoms::top, u""_ns);
if (rv == NS_ERROR_EDITOR_DESTROYED) {
NS_WARNING(
"CSSEditUtils::RemoveCSSPropertyWithTransaction(nsGkAtoms::top) "
"destroyed the editor");
return NS_ERROR_EDITOR_DESTROYED;
}
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rv),
"CSSEditUtils::RemoveCSSPropertyWithTransaction(nsGkAtoms::top) "
"failed, but ignored");
// MOZ_KnownLive(*styledElement): aElement's lifetime must be guarantted
// by the caller because of MOZ_CAN_RUN_SCRIPT method.
rv = CSSEditUtils::RemoveCSSPropertyWithTransaction(
*this, MOZ_KnownLive(*styledElement), *nsGkAtoms::left, u""_ns);
if (rv == NS_ERROR_EDITOR_DESTROYED) {
NS_WARNING(
"CSSEditUtils::RemoveCSSPropertyWithTransaction(nsGkAtoms::left) "
"destroyed the editor");
return NS_ERROR_EDITOR_DESTROYED;
}
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rv),
"CSSEditUtils::RemoveCSSPropertyWithTransaction(nsGkAtoms::left) "
"failed, but ignored");
// MOZ_KnownLive(*styledElement): aElement's lifetime must be guarantted
// by the caller because of MOZ_CAN_RUN_SCRIPT method.
rv = CSSEditUtils::RemoveCSSPropertyWithTransaction(
*this, MOZ_KnownLive(*styledElement), *nsGkAtoms::z_index, u""_ns);
if (rv == NS_ERROR_EDITOR_DESTROYED) {
NS_WARNING(
"CSSEditUtils::RemoveCSSPropertyWithTransaction(nsGkAtoms::z_index) "
"destroyed the editor");
return NS_ERROR_EDITOR_DESTROYED;
}
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rv),
"CSSEditUtils::RemoveCSSPropertyWithTransaction(nsGkAtoms::z_index) "
"failed, but ignored");
if (!HTMLEditUtils::IsImage(styledElement)) {
// MOZ_KnownLive(*styledElement): aElement's lifetime must be guarantted
// by the caller because of MOZ_CAN_RUN_SCRIPT method.
rv = CSSEditUtils::RemoveCSSPropertyWithTransaction(
*this, MOZ_KnownLive(*styledElement), *nsGkAtoms::width, u""_ns);
if (rv == NS_ERROR_EDITOR_DESTROYED) {
NS_WARNING(
"CSSEditUtils::RemoveCSSPropertyWithTransaction(nsGkAtoms::width) "
"destroyed the editor");
return NS_ERROR_EDITOR_DESTROYED;
}
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rv),
"CSSEditUtils::RemoveCSSPropertyWithTransaction(nsGkAtoms::width) "
"failed, but ignored");
// MOZ_KnownLive(*styledElement): aElement's lifetime must be guarantted
// by the caller because of MOZ_CAN_RUN_SCRIPT method.
rv = CSSEditUtils::RemoveCSSPropertyWithTransaction(
*this, MOZ_KnownLive(*styledElement), *nsGkAtoms::height, u""_ns);
if (rv == NS_ERROR_EDITOR_DESTROYED) {
NS_WARNING(
"CSSEditUtils::RemoveCSSPropertyWithTransaction(nsGkAtoms::height) "
"destroyed the editor");
return NS_ERROR_EDITOR_DESTROYED;
}
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rv),
"CSSEditUtils::RemoveCSSPropertyWithTransaction(nsGkAtoms::height) "
"failed, but ignored");
}
if (!styledElement->IsHTMLElement(nsGkAtoms::div) ||
HTMLEditor::HasStyleOrIdOrClassAttribute(*styledElement)) {
return NS_OK;
}
EditorDOMPoint pointToPutCaret;
// Make sure the first fild and last child of aElement starts/ends hard
// line(s) even after removing `aElement`.
{
// MOZ_KnownLive(*styledElement): aElement's lifetime must be guarantted
// by the caller because of MOZ_CAN_RUN_SCRIPT method.
Result<CreateElementResult, nsresult>
maybeInsertBRElementBeforeFirstChildResult =
EnsureHardLineBeginsWithFirstChildOf(MOZ_KnownLive(*styledElement));
if (MOZ_UNLIKELY(maybeInsertBRElementBeforeFirstChildResult.isErr())) {
NS_WARNING("HTMLEditor::EnsureHardLineBeginsWithFirstChildOf() failed");
return maybeInsertBRElementBeforeFirstChildResult.unwrapErr();
}
CreateElementResult unwrappedResult =
maybeInsertBRElementBeforeFirstChildResult.unwrap();
if (unwrappedResult.HasCaretPointSuggestion()) {
pointToPutCaret = unwrappedResult.UnwrapCaretPoint();
}
}
{
// MOZ_KnownLive(*styledElement): aElement's lifetime must be guarantted
// by the caller because of MOZ_CAN_RUN_SCRIPT method.
Result<CreateElementResult, nsresult>
maybeInsertBRElementAfterLastChildResult =
EnsureHardLineEndsWithLastChildOf(MOZ_KnownLive(*styledElement));
if (MOZ_UNLIKELY(maybeInsertBRElementAfterLastChildResult.isErr())) {
NS_WARNING("HTMLEditor::EnsureHardLineEndsWithLastChildOf() failed");
return maybeInsertBRElementAfterLastChildResult.unwrapErr();
}
CreateElementResult unwrappedResult =
maybeInsertBRElementAfterLastChildResult.unwrap();
if (unwrappedResult.HasCaretPointSuggestion()) {
pointToPutCaret = unwrappedResult.UnwrapCaretPoint();
}
}
{
// MOZ_KnownLive(*styledElement): aElement's lifetime must be guarantted
// by the caller because of MOZ_CAN_RUN_SCRIPT method.
Result<EditorDOMPoint, nsresult> unwrapStyledElementResult =
RemoveContainerWithTransaction(MOZ_KnownLive(*styledElement));
if (MOZ_UNLIKELY(unwrapStyledElementResult.isErr())) {
NS_WARNING("HTMLEditor::RemoveContainerWithTransaction() failed");
return unwrapStyledElementResult.unwrapErr();
}
if (unwrapStyledElementResult.inspect().IsSet()) {
pointToPutCaret = unwrapStyledElementResult.unwrap();
}
}
if (!AllowsTransactionsToChangeSelection() || !pointToPutCaret.IsSet()) {
return NS_OK;
}
rv = CollapseSelectionTo(pointToPutCaret);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"EditorBase::CollapseSelectionTo() failed");
return rv;
}
NS_IMETHODIMP HTMLEditor::SetSnapToGridEnabled(bool aEnabled) {
mSnapToGridEnabled = aEnabled;
return NS_OK;
}
NS_IMETHODIMP HTMLEditor::GetSnapToGridEnabled(bool* aIsEnabled) {
*aIsEnabled = mSnapToGridEnabled;
return NS_OK;
}
NS_IMETHODIMP HTMLEditor::SetGridSize(uint32_t aSize) {
mGridSize = aSize;
return NS_OK;
}
NS_IMETHODIMP HTMLEditor::GetGridSize(uint32_t* aSize) {
*aSize = mGridSize;
return NS_OK;
}
nsresult HTMLEditor::SetTopAndLeftWithTransaction(
nsStyledElement& aStyledElement, int32_t aX, int32_t aY) {
AutoPlaceholderBatch treatAsOneTransaction(
*this, ScrollSelectionIntoView::Yes, __FUNCTION__);
nsresult rv;
rv = CSSEditUtils::SetCSSPropertyPixelsWithTransaction(*this, aStyledElement,
*nsGkAtoms::left, aX);
if (rv == NS_ERROR_EDITOR_DESTROYED) {
NS_WARNING(
"CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::left) "
"destroyed the editor");
return NS_ERROR_EDITOR_DESTROYED;
}
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rv),
"CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::left) "
"failed, but ignored");
rv = CSSEditUtils::SetCSSPropertyPixelsWithTransaction(*this, aStyledElement,
*nsGkAtoms::top, aY);
if (rv == NS_ERROR_EDITOR_DESTROYED) {
NS_WARNING(
"CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::top) "
"destroyed the editor");
return NS_ERROR_EDITOR_DESTROYED;
}
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rv),
"CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::top) "
"failed, but ignored");
return NS_OK;
}
nsresult HTMLEditor::GetTemporaryStyleForFocusedPositionedElement(
Element& aElement, nsAString& aReturn) {
// we are going to outline the positioned element and bring it to the
// front to overlap any other element intersecting with it. But
// first, let's see what's the background and foreground colors of the
// positioned element.
// if background-image computed value is 'none,
// If the background color is 'auto' and R G B values of the foreground are
// each above #d0, use a black background
// If the background color is 'auto' and at least one of R G B values of
// the foreground is below #d0, use a white background
// Otherwise don't change background/foreground
aReturn.Truncate();
nsAutoString backgroundImageValue;
nsresult rv = CSSEditUtils::GetComputedProperty(
aElement, *nsGkAtoms::background_image, backgroundImageValue);
if (NS_FAILED(rv)) {
NS_WARNING(
"CSSEditUtils::GetComputedProperty(nsGkAtoms::background_image) "
"failed");
return rv;
}
if (!backgroundImageValue.EqualsLiteral("none")) {
return NS_OK;
}
nsAutoString backgroundColorValue;
rv = CSSEditUtils::GetComputedProperty(aElement, *nsGkAtoms::backgroundColor,
backgroundColorValue);
if (NS_FAILED(rv)) {
NS_WARNING(
"CSSEditUtils::GetComputedProperty(nsGkAtoms::backgroundColor) "
"failed");
return rv;
}
if (!backgroundColorValue.EqualsLiteral("rgba(0, 0, 0, 0)")) {
return NS_OK;
}
RefPtr<const ComputedStyle> style =
nsComputedDOMStyle::GetComputedStyle(&aElement);
if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}
if (!style) {
NS_WARNING("nsComputedDOMStyle::GetComputedStyle() failed");
return NS_ERROR_FAILURE;
}
static const uint8_t kBlackBgTrigger = 0xd0;
auto color = style->StyleText()->mColor.ToColor();
if (NS_GET_R(color) >= kBlackBgTrigger &&
NS_GET_G(color) >= kBlackBgTrigger &&
NS_GET_B(color) >= kBlackBgTrigger) {
aReturn.AssignLiteral("black");
} else {
aReturn.AssignLiteral("white");
}
return NS_OK;
}
} // namespace mozilla