/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* 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 "ErrorList.h" #include "HTMLEditor.h" #include "CSSEditUtils.h" #include "HTMLEditorEventListener.h" #include "HTMLEditUtils.h" #include "mozilla/DebugOnly.h" #include "mozilla/LookAndFeel.h" #include "mozilla/MathAlgorithms.h" #include "mozilla/mozalloc.h" #include "mozilla/Preferences.h" #include "mozilla/PresShell.h" #include "mozilla/StaticPrefs_editor.h" #include "mozilla/dom/Event.h" #include "mozilla/dom/MouseEvent.h" #include "mozilla/dom/EventTarget.h" #include "nsAString.h" #include "nsAlgorithm.h" #include "nsCOMPtr.h" #include "nsDebug.h" #include "nsError.h" #include "nsGkAtoms.h" #include "nsAtom.h" #include "nsIContent.h" #include "nsID.h" #include "mozilla/dom/Document.h" #include "nsISupportsUtils.h" #include "nsPIDOMWindow.h" #include "nsReadableUtils.h" #include "nsString.h" #include "nsStringFwd.h" #include "nsStyledElement.h" #include "nsTextNode.h" #include "nscore.h" #include #define kTopLeft u"nw"_ns #define kTop u"n"_ns #define kTopRight u"ne"_ns #define kLeft u"w"_ns #define kRight u"e"_ns #define kBottomLeft u"sw"_ns #define kBottom u"s"_ns #define kBottomRight u"se"_ns namespace mozilla { using namespace dom; /****************************************************************************** * mozilla::HTMLEditor ******************************************************************************/ ManualNACPtr HTMLEditor::CreateResizer(int16_t aLocation, nsIContent& aParentContent) { ManualNACPtr resizer = CreateAnonymousElement(nsGkAtoms::span, aParentContent, u"mozResizer"_ns, false); if (!resizer) { NS_WARNING( "HTMLEditor::CreateAnonymousElement(nsGkAtoms::span, mozResizer) " "failed"); return nullptr; } // add the mouse listener so we can detect a click on a resizer DebugOnly rvIgnored = resizer->AddEventListener(u"mousedown"_ns, mEventListener, true); NS_WARNING_ASSERTION( NS_SUCCEEDED(rvIgnored), "EventTarget::AddEventListener(mousedown) failed, but ignored"); nsAutoString locationStr; switch (aLocation) { case nsIHTMLObjectResizer::eTopLeft: locationStr = kTopLeft; break; case nsIHTMLObjectResizer::eTop: locationStr = kTop; break; case nsIHTMLObjectResizer::eTopRight: locationStr = kTopRight; break; case nsIHTMLObjectResizer::eLeft: locationStr = kLeft; break; case nsIHTMLObjectResizer::eRight: locationStr = kRight; break; case nsIHTMLObjectResizer::eBottomLeft: locationStr = kBottomLeft; break; case nsIHTMLObjectResizer::eBottom: locationStr = kBottom; break; case nsIHTMLObjectResizer::eBottomRight: locationStr = kBottomRight; break; } if (NS_FAILED(resizer->SetAttr(kNameSpaceID_None, nsGkAtoms::anonlocation, locationStr, true))) { NS_WARNING("Element::SetAttr(nsGkAtoms::anonlocation) failed"); return nullptr; } return resizer; } ManualNACPtr HTMLEditor::CreateShadow(nsIContent& aParentContent, Element& aOriginalObject) { // let's create an image through the element factory RefPtr name; if (HTMLEditUtils::IsImage(&aOriginalObject)) { name = nsGkAtoms::img; } else { name = nsGkAtoms::span; } return CreateAnonymousElement(name, aParentContent, u"mozResizingShadow"_ns, true); } ManualNACPtr HTMLEditor::CreateResizingInfo(nsIContent& aParentContent) { // let's create an info box through the element factory return CreateAnonymousElement(nsGkAtoms::span, aParentContent, u"mozResizingInfo"_ns, true); } nsresult HTMLEditor::SetAllResizersPosition() { if (NS_WARN_IF(!mTopLeftHandle)) { return NS_ERROR_FAILURE; // There are no resizers. } int32_t x = mResizedObjectX; int32_t y = mResizedObjectY; int32_t w = mResizedObjectWidth; int32_t h = mResizedObjectHeight; nsAutoString value; float resizerWidth, resizerHeight; RefPtr dummyUnit; DebugOnly rvIgnored = NS_OK; OwningNonNull topLeftHandle = *mTopLeftHandle.get(); // XXX Do we really need to computed value rather than specified value? // Because it's an anonymous node. rvIgnored = CSSEditUtils::GetComputedProperty(*topLeftHandle, *nsGkAtoms::width, value); if (NS_WARN_IF(Destroyed())) { return NS_ERROR_EDITOR_DESTROYED; } if (NS_WARN_IF(topLeftHandle != mTopLeftHandle)) { return NS_ERROR_FAILURE; } NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored), "CSSEditUtils::GetComputedProperty(nsGkAtoms::width) " "failed, but ignored"); rvIgnored = CSSEditUtils::GetComputedProperty(*topLeftHandle, *nsGkAtoms::height, value); if (NS_WARN_IF(Destroyed())) { return NS_ERROR_EDITOR_DESTROYED; } if (NS_WARN_IF(topLeftHandle != mTopLeftHandle)) { return NS_ERROR_FAILURE; } NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored), "CSSEditUtils::GetComputedProperty(nsGkAtoms::height) " "failed, but ignored"); CSSEditUtils::ParseLength(value, &resizerWidth, getter_AddRefs(dummyUnit)); CSSEditUtils::ParseLength(value, &resizerHeight, getter_AddRefs(dummyUnit)); int32_t rw = static_cast((resizerWidth + 1) / 2); int32_t rh = static_cast((resizerHeight + 1) / 2); // While moving each resizer, mutation event listener may hide the resizers. // And in worst case, new resizers may be recreated. So, we need to store // all resizers here, and then, if we detect a resizer is removed or replaced, // we should do nothing anymore. // FYI: Note that only checking if mTopLeftHandle is replaced is enough. // We're may be in hot path if user resizes an element a lot. So, // we should just add-ref mTopLeftHandle. auto setHandlePosition = [this](ManualNACPtr& aHandleElement, int32_t aNewX, int32_t aNewY) MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION -> nsresult { RefPtr handleStyledElement = nsStyledElement::FromNodeOrNull(aHandleElement.get()); if (!handleStyledElement) { return NS_OK; } nsresult rv = SetAnonymousElementPositionWithoutTransaction( *handleStyledElement, aNewX, aNewY); if (NS_FAILED(rv)) { NS_WARNING( "HTMLEditor::SetAnonymousElementPositionWithoutTransaction() " "failed"); return rv; } return NS_WARN_IF(handleStyledElement != aHandleElement.get()) ? NS_ERROR_FAILURE : NS_OK; }; nsresult rv; rv = setHandlePosition(mTopLeftHandle, x - rw, y - rh); if (NS_FAILED(rv)) { NS_WARNING("Failed to set top-left handle position"); return rv; } rv = setHandlePosition(mTopHandle, x + w / 2 - rw, y - rh); if (NS_FAILED(rv)) { NS_WARNING("Failed to set top handle position"); return rv; } rv = setHandlePosition(mTopRightHandle, x + w - rw - 1, y - rh); if (NS_FAILED(rv)) { NS_WARNING("Failed to set top-right handle position"); return rv; } rv = setHandlePosition(mLeftHandle, x - rw, y + h / 2 - rh); if (NS_FAILED(rv)) { NS_WARNING("Failed to set left handle position"); return rv; } rv = setHandlePosition(mRightHandle, x + w - rw - 1, y + h / 2 - rh); if (NS_FAILED(rv)) { NS_WARNING("Failed to set right handle position"); return rv; } rv = setHandlePosition(mBottomLeftHandle, x - rw, y + h - rh - 1); if (NS_FAILED(rv)) { NS_WARNING("Failed to set bottom-left handle position"); return rv; } rv = setHandlePosition(mBottomHandle, x + w / 2 - rw, y + h - rh - 1); if (NS_FAILED(rv)) { NS_WARNING("Failed to set bottom handle position"); return rv; } rv = setHandlePosition(mBottomRightHandle, x + w - rw - 1, y + h - rh - 1); if (NS_FAILED(rv)) { NS_WARNING("Failed to set bottom-right handle position"); return rv; } return NS_OK; } nsresult HTMLEditor::RefreshResizers() { AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing); if (NS_WARN_IF(!editActionData.CanHandle())) { return NS_ERROR_NOT_INITIALIZED; } nsresult rv = RefreshResizersInternal(); NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "HTMLEditor::RefreshResizersInternal() failed"); return EditorBase::ToGenericNSResult(rv); } nsresult HTMLEditor::RefreshResizersInternal() { MOZ_ASSERT(IsEditActionDataAvailable()); // Don't warn even if resizers are not visible since script cannot check // if they are visible and this is non-virtual method. So, the cost of // calling this can be ignored. if (!mResizedObject) { return NS_OK; } OwningNonNull resizedObject = *mResizedObject; nsresult rv = GetPositionAndDimensions( resizedObject, mResizedObjectX, mResizedObjectY, mResizedObjectWidth, mResizedObjectHeight, mResizedObjectBorderLeft, mResizedObjectBorderTop, mResizedObjectMarginLeft, mResizedObjectMarginTop); if (NS_FAILED(rv)) { NS_WARNING("HTMLEditor::GetPositionAndDimensions() failed"); return rv; } if (NS_WARN_IF(resizedObject != mResizedObject)) { return NS_ERROR_FAILURE; } rv = SetAllResizersPosition(); if (NS_FAILED(rv)) { NS_WARNING("HTMLEditor::SetAllResizersPosition() failed"); return rv; } if (NS_WARN_IF(resizedObject != mResizedObject)) { return NS_ERROR_FAILURE; } MOZ_ASSERT( mResizingShadow, "SetAllResizersPosition() should return error if resizers are hidden"); RefPtr resizingShadow = mResizingShadow.get(); rv = SetShadowPosition(*resizingShadow, resizedObject, mResizedObjectX, mResizedObjectY); if (NS_FAILED(rv)) { NS_WARNING("HTMLEditor::SetShadowPosition() failed"); return rv; } if (NS_WARN_IF(resizedObject != mResizedObject)) { return NS_ERROR_FAILURE; } return NS_OK; } nsresult HTMLEditor::ShowResizersInternal(Element& aResizedElement) { // When we have visible resizers, we cannot show new resizers. // So, the caller should call HideResizersInternal() first if this // returns error. if (NS_WARN_IF(mResizedObject)) { return NS_ERROR_UNEXPECTED; } nsCOMPtr parentContent = aResizedElement.GetParent(); if (NS_WARN_IF(!parentContent)) { return NS_ERROR_FAILURE; } const RefPtr editingHost = ComputeEditingHost(); if (NS_WARN_IF(!editingHost) || NS_WARN_IF(!aResizedElement.IsInclusiveDescendantOf(editingHost))) { return NS_ERROR_UNEXPECTED; } // Let's create and setup resizers. If we failed something, we should // cancel everything which we do in this method. do { mResizedObject = &aResizedElement; // The resizers and the shadow will be anonymous siblings of the element. // Note that creating a resizer or shadow may causes calling // HideRisizersInternal() via a mutation event listener. So, we should // store new resizer to a local variable, then, check: // - whether creating resizer is already set to the member or not // - whether resizing element is changed to another element // If showing resizers are canceled, we hit the latter check. // If resizers for another element is shown during this, we hit the latter // check too. // If resizers are just shown again for same element, we hit the former // check. ManualNACPtr newResizer = CreateResizer(nsIHTMLObjectResizer::eTopLeft, *parentContent); if (!newResizer) { NS_WARNING("HTMLEditor::CreateResizer(eTopLeft) failed"); break; } if (NS_WARN_IF(mTopLeftHandle) || NS_WARN_IF(mResizedObject != &aResizedElement)) { // Don't hide current resizers in this case because they are not what // we're creating. return NS_ERROR_FAILURE; } mTopLeftHandle = std::move(newResizer); newResizer = CreateResizer(nsIHTMLObjectResizer::eTop, *parentContent); if (!newResizer) { NS_WARNING("HTMLEditor::CreateResizer(eTop) failed"); break; } if (NS_WARN_IF(mTopHandle) || NS_WARN_IF(mResizedObject != &aResizedElement)) { return NS_ERROR_FAILURE; } mTopHandle = std::move(newResizer); newResizer = CreateResizer(nsIHTMLObjectResizer::eTopRight, *parentContent); if (!newResizer) { NS_WARNING("HTMLEditor::CreateResizer(eTopRight) failed"); break; } if (NS_WARN_IF(mTopRightHandle) || NS_WARN_IF(mResizedObject != &aResizedElement)) { return NS_ERROR_FAILURE; } mTopRightHandle = std::move(newResizer); newResizer = CreateResizer(nsIHTMLObjectResizer::eLeft, *parentContent); if (!newResizer) { NS_WARNING("HTMLEditor::CreateResizer(eLeft) failed"); break; } if (NS_WARN_IF(mLeftHandle) || NS_WARN_IF(mResizedObject != &aResizedElement)) { return NS_ERROR_FAILURE; } mLeftHandle = std::move(newResizer); newResizer = CreateResizer(nsIHTMLObjectResizer::eRight, *parentContent); if (!newResizer) { NS_WARNING("HTMLEditor::CreateResizer(eRight) failed"); break; } if (NS_WARN_IF(mRightHandle) || NS_WARN_IF(mResizedObject != &aResizedElement)) { return NS_ERROR_FAILURE; } mRightHandle = std::move(newResizer); newResizer = CreateResizer(nsIHTMLObjectResizer::eBottomLeft, *parentContent); if (!newResizer) { NS_WARNING("HTMLEditor::CreateResizer(eBottomLeft) failed"); break; } if (NS_WARN_IF(mBottomLeftHandle) || NS_WARN_IF(mResizedObject != &aResizedElement)) { return NS_ERROR_FAILURE; } mBottomLeftHandle = std::move(newResizer); newResizer = CreateResizer(nsIHTMLObjectResizer::eBottom, *parentContent); if (!newResizer) { NS_WARNING("HTMLEditor::CreateResizer(eBottom) failed"); break; } if (NS_WARN_IF(mBottomHandle) || NS_WARN_IF(mResizedObject != &aResizedElement)) { return NS_ERROR_FAILURE; } mBottomHandle = std::move(newResizer); newResizer = CreateResizer(nsIHTMLObjectResizer::eBottomRight, *parentContent); if (!newResizer) { NS_WARNING("HTMLEditor::CreateResizer(eBottomRight) failed"); break; } if (NS_WARN_IF(mBottomRightHandle) || NS_WARN_IF(mResizedObject != &aResizedElement)) { return NS_ERROR_FAILURE; } mBottomRightHandle = std::move(newResizer); // Store the last resizer which we created. This is useful when we // need to check whether our resizers are hiddedn and recreated another // set of resizers or not. RefPtr createdBottomRightHandle = mBottomRightHandle.get(); nsresult rv = GetPositionAndDimensions( aResizedElement, mResizedObjectX, mResizedObjectY, mResizedObjectWidth, mResizedObjectHeight, mResizedObjectBorderLeft, mResizedObjectBorderTop, mResizedObjectMarginLeft, mResizedObjectMarginTop); if (NS_FAILED(rv)) { NS_WARNING("HTMLEditor::GetPositionAndDimensions() failed"); break; } // and let's set their absolute positions in the document rv = SetAllResizersPosition(); if (NS_FAILED(rv)) { NS_WARNING("HTMLEditor::SetAllResizersPosition() failed"); if (NS_WARN_IF(mBottomRightHandle.get() != createdBottomRightHandle)) { return NS_ERROR_FAILURE; } break; } // now, let's create the resizing shadow ManualNACPtr newShadow = CreateShadow(*parentContent, aResizedElement); if (!newShadow) { NS_WARNING("HTMLEditor::CreateShadow() failed"); break; } if (NS_WARN_IF(mResizingShadow) || NS_WARN_IF(mResizedObject != &aResizedElement)) { return NS_ERROR_FAILURE; } mResizingShadow = std::move(newShadow); // and set its position RefPtr resizingShadow = mResizingShadow.get(); rv = SetShadowPosition(*resizingShadow, aResizedElement, mResizedObjectX, mResizedObjectY); if (NS_FAILED(rv)) { NS_WARNING("HTMLEditor::SetShadowPosition() failed"); if (NS_WARN_IF(mBottomRightHandle.get() != createdBottomRightHandle)) { return NS_ERROR_FAILURE; } break; } // and then the resizing info tooltip ManualNACPtr newResizingInfo = CreateResizingInfo(*parentContent); if (!newResizingInfo) { NS_WARNING("HTMLEditor::CreateResizingInfo() failed"); break; } if (NS_WARN_IF(mResizingInfo) || NS_WARN_IF(mResizedObject != &aResizedElement)) { return NS_ERROR_FAILURE; } mResizingInfo = std::move(newResizingInfo); // and listen to the "resize" event on the window first, get the // window from the document... if (NS_WARN_IF(!mEventListener)) { break; } rv = static_cast(mEventListener.get()) ->ListenToWindowResizeEvent(true); if (NS_FAILED(rv)) { NS_WARNING( "HTMLEditorEventListener::ListenToWindowResizeEvent(true) failed"); break; } MOZ_ASSERT(mResizedObject == &aResizedElement); // XXX Even when it failed to add event listener, should we need to set // _moz_resizing attribute? DebugOnly 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); DebugOnly rv = HideResizersInternal(); NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "HTMLEditor::HideResizersInternal() failed to clean up"); return NS_ERROR_FAILURE; } NS_IMETHODIMP HTMLEditor::HideResizers() { AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing); if (NS_WARN_IF(!editActionData.CanHandle())) { return NS_ERROR_NOT_INITIALIZED; } nsresult rv = HideResizersInternal(); NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "HTMLEditor::HideResizersInternal() failed"); return EditorBase::ToGenericNSResult(rv); } nsresult HTMLEditor::HideResizersInternal() { // Don't warn even if resizers are visible since script cannot check // if they are visible and this is non-virtual method. So, the cost of // calling this can be ignored. if (!mResizedObject) { return NS_OK; } // get the presshell's document observer interface. RefPtr presShell = GetPresShell(); NS_WARNING_ASSERTION(presShell, "There is no presShell"); // 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. constexpr auto mousedown = u"mousedown"_ns; // HTMLEditor should forget all members related to resizers first since // removing a part of UI may cause showing the resizers again. In such // case, the members may be overwritten by ShowResizers() and this will // lose the chance to release the old resizers. ManualNACPtr topLeftHandle(std::move(mTopLeftHandle)); ManualNACPtr topHandle(std::move(mTopHandle)); ManualNACPtr topRightHandle(std::move(mTopRightHandle)); ManualNACPtr leftHandle(std::move(mLeftHandle)); ManualNACPtr rightHandle(std::move(mRightHandle)); ManualNACPtr bottomLeftHandle(std::move(mBottomLeftHandle)); ManualNACPtr bottomHandle(std::move(mBottomHandle)); ManualNACPtr bottomRightHandle(std::move(mBottomRightHandle)); ManualNACPtr resizingShadow(std::move(mResizingShadow)); ManualNACPtr resizingInfo(std::move(mResizingInfo)); RefPtr activatedHandle(std::move(mActivatedHandle)); RefPtr resizedObject(std::move(mResizedObject)); // Remvoe all handles. RemoveListenerAndDeleteRef(mousedown, mEventListener, true, std::move(topLeftHandle), presShell); RemoveListenerAndDeleteRef(mousedown, mEventListener, true, std::move(topHandle), presShell); RemoveListenerAndDeleteRef(mousedown, mEventListener, true, std::move(topRightHandle), presShell); RemoveListenerAndDeleteRef(mousedown, mEventListener, true, std::move(leftHandle), presShell); RemoveListenerAndDeleteRef(mousedown, mEventListener, true, std::move(rightHandle), presShell); RemoveListenerAndDeleteRef(mousedown, mEventListener, true, std::move(bottomLeftHandle), presShell); RemoveListenerAndDeleteRef(mousedown, mEventListener, true, std::move(bottomHandle), presShell); RemoveListenerAndDeleteRef(mousedown, mEventListener, true, std::move(bottomRightHandle), presShell); RemoveListenerAndDeleteRef(mousedown, mEventListener, true, std::move(resizingShadow), presShell); RemoveListenerAndDeleteRef(mousedown, mEventListener, true, std::move(resizingInfo), presShell); // Remove active state of a resizer. if (activatedHandle) { DebugOnly rvIgnored = activatedHandle->UnsetAttr( kNameSpaceID_None, nsGkAtoms::_moz_activated, true); NS_WARNING_ASSERTION( NS_SUCCEEDED(rvIgnored), "Element::UnsetAttr(nsGkAtoms::_moz_activated) failed, but ignored"); } // Remove resizing state of the target element. DebugOnly 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; } nsresult rv = static_cast(mEventListener.get()) ->ListenToMouseMoveEventForResizers(false); if (NS_FAILED(rv)) { NS_WARNING( "HTMLEditorEventListener::ListenToMouseMoveEventForResizers(false) " "failed"); return rv; } // Remove resize event listener from the window. if (!mEventListener) { return NS_OK; } rv = static_cast(mEventListener.get()) ->ListenToWindowResizeEvent(false); NS_WARNING_ASSERTION( NS_SUCCEEDED(rv), "HTMLEditorEventListener::ListenToWindowResizeEvent(false) failed"); return rv; } void HTMLEditor::HideShadowAndInfo() { if (mResizingShadow) { DebugOnly rvIgnored = mResizingShadow->SetAttr( kNameSpaceID_None, nsGkAtoms::_class, u"hidden"_ns, true); NS_WARNING_ASSERTION( NS_SUCCEEDED(rvIgnored), "Element::SetAttr(nsGkAtoms::_class, hidden) failed, but ignored"); } if (mResizingInfo) { DebugOnly rvIgnored = mResizingInfo->SetAttr( kNameSpaceID_None, nsGkAtoms::_class, u"hidden"_ns, true); NS_WARNING_ASSERTION( NS_SUCCEEDED(rvIgnored), "Element::SetAttr(nsGkAtoms::_class, hidden) failed, but ignored"); } } nsresult HTMLEditor::StartResizing(Element& aHandleElement) { mIsResizing = true; mActivatedHandle = &aHandleElement; DebugOnly rvIgnored = mActivatedHandle->SetAttr( kNameSpaceID_None, nsGkAtoms::_moz_activated, u"true"_ns, true); 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); // the way we change the position/size of the shadow depends on // the handle nsAutoString locationStr; mActivatedHandle->GetAttr(nsGkAtoms::anonlocation, locationStr); if (locationStr.Equals(kTopLeft)) { SetResizeIncrements(1, 1, -1, -1, preserveRatio); } else if (locationStr.Equals(kTop)) { SetResizeIncrements(0, 1, 0, -1, false); } else if (locationStr.Equals(kTopRight)) { SetResizeIncrements(0, 1, 1, -1, preserveRatio); } else if (locationStr.Equals(kLeft)) { SetResizeIncrements(1, 0, -1, 0, false); } else if (locationStr.Equals(kRight)) { SetResizeIncrements(0, 0, 1, 0, false); } else if (locationStr.Equals(kBottomLeft)) { SetResizeIncrements(1, 0, -1, 1, preserveRatio); } else if (locationStr.Equals(kBottom)) { SetResizeIncrements(0, 0, 0, 1, false); } else if (locationStr.Equals(kBottomRight)) { SetResizeIncrements(0, 0, 1, 1, preserveRatio); } // make the shadow appear rvIgnored = mResizingShadow->UnsetAttr(kNameSpaceID_None, nsGkAtoms::_class, true); if (NS_WARN_IF(Destroyed())) { return NS_ERROR_EDITOR_DESTROYED; } NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored), "Element::UnsetAttr(nsGkAtoms::_class) failed"); // position it if (RefPtr resizingShadowStyledElement = nsStyledElement::FromNodeOrNull(mResizingShadow.get())) { nsresult rv; rv = CSSEditUtils::SetCSSPropertyPixelsWithoutTransaction( *resizingShadowStyledElement, *nsGkAtoms::width, mResizedObjectWidth); 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"); rv = CSSEditUtils::SetCSSPropertyPixelsWithoutTransaction( *resizingShadowStyledElement, *nsGkAtoms::height, mResizedObjectHeight); 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"); } // add a mouse move listener to the editor if (NS_WARN_IF(!mEventListener)) { return NS_ERROR_NOT_INITIALIZED; } nsresult rv = static_cast(mEventListener.get()) ->ListenToMouseMoveEventForResizers(true); NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "HTMLEditorEventListener::" "ListenToMouseMoveEventForResizers(true) failed"); return rv; } nsresult HTMLEditor::StartToDragResizerOrHandleDragGestureOnGrabber( MouseEvent& aMouseDownEvent, Element& aEventTargetElement) { MOZ_ASSERT(aMouseDownEvent.GetExplicitOriginalTarget() == &aEventTargetElement); MOZ_ASSERT(!aMouseDownEvent.DefaultPrevented()); MOZ_ASSERT(aMouseDownEvent.WidgetEventPtr()->mMessage == eMouseDown); nsAutoString anonclass; aEventTargetElement.GetAttr(nsGkAtoms::_moz_anonclass, anonclass); if (anonclass.EqualsLiteral("mozResizer")) { AutoEditActionDataSetter editActionData(*this, EditAction::eResizingElement); if (NS_WARN_IF(!editActionData.CanHandle())) { return NS_ERROR_NOT_INITIALIZED; } // If we have an anonymous element and that element is a resizer, // let's start resizing! aMouseDownEvent.PreventDefault(); mOriginalX = aMouseDownEvent.ClientX(); mOriginalY = aMouseDownEvent.ClientY(); nsresult rv = StartResizing(aEventTargetElement); NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "HTMLEditor::StartResizing() failed"); return EditorBase::ToGenericNSResult(rv); } if (anonclass.EqualsLiteral("mozGrabber")) { AutoEditActionDataSetter editActionData(*this, EditAction::eMovingElement); if (NS_WARN_IF(!editActionData.CanHandle())) { return NS_ERROR_NOT_INITIALIZED; } // If we have an anonymous element and that element is a grabber, // let's start moving the element! mOriginalX = aMouseDownEvent.ClientX(); mOriginalY = aMouseDownEvent.ClientY(); nsresult rv = GrabberClicked(); NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "HTMLEditor::GrabberClicked() failed"); return EditorBase::ToGenericNSResult(rv); } return NS_OK; } nsresult HTMLEditor::StopDraggingResizerOrGrabberAt( const CSSIntPoint& aClientPoint) { if (mIsResizing) { AutoEditActionDataSetter editActionData(*this, EditAction::eResizeElement); if (NS_WARN_IF(!editActionData.CanHandle())) { return NS_ERROR_NOT_INITIALIZED; } // we are resizing and release the mouse button, so let's // end the resizing process mIsResizing = false; HideShadowAndInfo(); nsresult rv = editActionData.MaybeDispatchBeforeInputEvent(); if (NS_FAILED(rv)) { NS_WARNING_ASSERTION( rv == NS_ERROR_EDITOR_ACTION_CANCELED, "EditorBase::MaybeDispatchBeforeInputEvent(), failed"); return EditorBase::ToGenericNSResult(rv); } rv = SetFinalSizeWithTransaction(aClientPoint.x, aClientPoint.y); if (rv == NS_ERROR_EDITOR_DESTROYED) { NS_WARNING( "HTMLEditor::SetFinalSizeWithTransaction() destroyed the editor"); return NS_ERROR_EDITOR_DESTROYED; } NS_WARNING_ASSERTION( NS_SUCCEEDED(rv), "HTMLEditor::SetFinalSizeWithTransaction() failed, but ignored"); return NS_OK; } if (mIsMoving || mGrabberClicked) { AutoEditActionDataSetter editActionData(*this, EditAction::eMoveElement); nsresult rv = editActionData.CanHandleAndMaybeDispatchBeforeInputEvent(); if (rv != NS_ERROR_EDITOR_ACTION_CANCELED && NS_WARN_IF(NS_FAILED(rv))) { NS_WARNING("CanHandleAndMaybeDispatchBeforeInputEvent() failed"); return EditorBase::ToGenericNSResult(rv); } if (mIsMoving) { DebugOnly rvIgnored = mPositioningShadow->SetAttr( kNameSpaceID_None, nsGkAtoms::_class, u"hidden"_ns, true); NS_WARNING_ASSERTION( NS_SUCCEEDED(rvIgnored), "Element::SetAttr(nsGkAtoms::_class, hidden) failed"); if (rv != NS_ERROR_EDITOR_ACTION_CANCELED) { SetFinalPosition(aClientPoint.x, aClientPoint.y); } } if (mGrabberClicked) { DebugOnly rvIgnored = EndMoving(); NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored), "HTMLEditor::EndMoving() failed"); } return EditorBase::ToGenericNSResult(rv); } return NS_OK; } void HTMLEditor::SetResizeIncrements(int32_t aX, int32_t aY, int32_t aW, int32_t aH, bool aPreserveRatio) { mXIncrementFactor = aX; mYIncrementFactor = aY; mWidthIncrementFactor = aW; mHeightIncrementFactor = aH; mPreserveRatio = aPreserveRatio; } nsresult HTMLEditor::SetResizingInfoPosition(int32_t aX, int32_t aY, int32_t aW, int32_t aH) { // Determine the position of the resizing info box based upon the new // position and size of the element (aX, aY, aW, aH), and which // resizer is the "activated handle". For example, place the resizing // info box at the bottom-right corner of the new element, if the element // is being resized by the bottom-right resizer. int32_t infoXPosition; int32_t infoYPosition; if (mActivatedHandle == mTopLeftHandle || mActivatedHandle == mLeftHandle || mActivatedHandle == mBottomLeftHandle) { infoXPosition = aX; } else if (mActivatedHandle == mTopHandle || mActivatedHandle == mBottomHandle) { infoXPosition = aX + (aW / 2); } else { // should only occur when mActivatedHandle is one of the 3 right-side // handles, but this is a reasonable default if it isn't any of them (?) infoXPosition = aX + aW; } if (mActivatedHandle == mTopLeftHandle || mActivatedHandle == mTopHandle || mActivatedHandle == mTopRightHandle) { infoYPosition = aY; } else if (mActivatedHandle == mLeftHandle || mActivatedHandle == mRightHandle) { infoYPosition = aY + (aH / 2); } else { // should only occur when mActivatedHandle is one of the 3 bottom-side // handles, but this is a reasonable default if it isn't any of them (?) infoYPosition = aY + aH; } // Offset info box by 20 so it's not directly under the mouse cursor. const int mouseCursorOffset = 20; if (RefPtr resizingInfoStyledElement = nsStyledElement::FromNodeOrNull(mResizingInfo.get())) { nsresult rv; rv = CSSEditUtils::SetCSSPropertyPixelsWithoutTransaction( *resizingInfoStyledElement, *nsGkAtoms::left, infoXPosition + mouseCursorOffset); 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::SetCSSPropertyPixelsWithoutTransaction( *resizingInfoStyledElement, *nsGkAtoms::top, infoYPosition + mouseCursorOffset); 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"); } nsCOMPtr textInfo = mResizingInfo->GetFirstChild(); ErrorResult error; if (textInfo) { mResizingInfo->RemoveChild(*textInfo, error); if (error.Failed()) { NS_WARNING("nsINode::RemoveChild() failed"); return error.StealNSResult(); } textInfo = nullptr; } nsAutoString widthStr, heightStr, diffWidthStr, diffHeightStr; widthStr.AppendInt(aW); heightStr.AppendInt(aH); int32_t diffWidth = aW - mResizedObjectWidth; int32_t diffHeight = aH - mResizedObjectHeight; if (diffWidth > 0) { diffWidthStr.Assign('+'); } if (diffHeight > 0) { diffHeightStr.Assign('+'); } diffWidthStr.AppendInt(diffWidth); diffHeightStr.AppendInt(diffHeight); nsAutoString info(widthStr + u" x "_ns + heightStr + u" ("_ns + diffWidthStr + u", "_ns + diffHeightStr + u")"_ns); RefPtr document = GetDocument(); textInfo = document->CreateTextNode(info); if (!textInfo) { NS_WARNING("Document::CreateTextNode() failed"); return NS_ERROR_FAILURE; } mResizingInfo->AppendChild(*textInfo, error); if (error.Failed()) { NS_WARNING("nsINode::AppendChild() failed"); return error.StealNSResult(); } nsresult rv = mResizingInfo->UnsetAttr(kNameSpaceID_None, nsGkAtoms::_class, true); NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Element::UnsetAttr(nsGkAtoms::_class) failed"); return rv; } nsresult HTMLEditor::SetShadowPosition(Element& aShadowElement, Element& aElement, int32_t aElementX, int32_t aElementY) { MOZ_ASSERT(&aShadowElement == mResizingShadow || &aShadowElement == mPositioningShadow); RefPtr handlingShadowElement = &aShadowElement == mResizingShadow ? mResizingShadow.get() : mPositioningShadow.get(); if (nsStyledElement* styledShadowElement = nsStyledElement::FromNode(&aShadowElement)) { // MOZ_KnownLive(*styledShadowElement): It's aShadowElement whose lifetime // must be guaranteed by caller because of MOZ_CAN_RUN_SCRIPT method. nsresult rv = SetAnonymousElementPositionWithoutTransaction( MOZ_KnownLive(*styledShadowElement), aElementX, aElementY); if (NS_FAILED(rv)) { NS_WARNING( "HTMLEditor::SetAnonymousElementPositionWithoutTransaction() " "failed"); return rv; } } if (!HTMLEditUtils::IsImage(&aElement)) { return NS_OK; } nsAutoString imageSource; aElement.GetAttr(kNameSpaceID_None, nsGkAtoms::src, imageSource); nsresult rv = aShadowElement.SetAttr(kNameSpaceID_None, nsGkAtoms::src, imageSource, true); if (NS_WARN_IF(Destroyed())) { return NS_ERROR_EDITOR_DESTROYED; } if (NS_FAILED(rv)) { NS_WARNING("Element::SetAttr(nsGkAtoms::src) failed"); return NS_ERROR_FAILURE; } return NS_WARN_IF(&aShadowElement != handlingShadowElement) ? NS_ERROR_FAILURE : NS_OK; } int32_t HTMLEditor::GetNewResizingIncrement(int32_t aX, int32_t aY, ResizeAt aResizeAt) const { int32_t result = 0; if (!mPreserveRatio) { switch (aResizeAt) { case ResizeAt::eX: case ResizeAt::eWidth: result = aX - mOriginalX; break; case ResizeAt::eY: case ResizeAt::eHeight: result = aY - mOriginalY; break; default: MOZ_ASSERT_UNREACHABLE("Invalid resizing request"); } return result; } int32_t xi = (aX - mOriginalX) * mWidthIncrementFactor; int32_t yi = (aY - mOriginalY) * mHeightIncrementFactor; float objectSizeRatio = ((float)mResizedObjectWidth) / ((float)mResizedObjectHeight); result = (xi > yi) ? xi : yi; switch (aResizeAt) { case ResizeAt::eX: case ResizeAt::eWidth: if (result == yi) result = (int32_t)(((float)result) * objectSizeRatio); result = (int32_t)(((float)result) * mWidthIncrementFactor); break; case ResizeAt::eY: case ResizeAt::eHeight: if (result == xi) result = (int32_t)(((float)result) / objectSizeRatio); result = (int32_t)(((float)result) * mHeightIncrementFactor); break; } return result; } int32_t HTMLEditor::GetNewResizingX(int32_t aX, int32_t aY) { int32_t resized = mResizedObjectX + GetNewResizingIncrement(aX, aY, ResizeAt::eX) * mXIncrementFactor; int32_t max = mResizedObjectX + mResizedObjectWidth; return std::min(resized, max); } int32_t HTMLEditor::GetNewResizingY(int32_t aX, int32_t aY) { int32_t resized = mResizedObjectY + GetNewResizingIncrement(aX, aY, ResizeAt::eY) * mYIncrementFactor; int32_t max = mResizedObjectY + mResizedObjectHeight; return std::min(resized, max); } int32_t HTMLEditor::GetNewResizingWidth(int32_t aX, int32_t aY) { int32_t resized = mResizedObjectWidth + GetNewResizingIncrement(aX, aY, ResizeAt::eWidth) * mWidthIncrementFactor; return std::max(resized, 1); } int32_t HTMLEditor::GetNewResizingHeight(int32_t aX, int32_t aY) { int32_t resized = mResizedObjectHeight + GetNewResizingIncrement(aX, aY, ResizeAt::eHeight) * mHeightIncrementFactor; return std::max(resized, 1); } nsresult HTMLEditor::UpdateResizerOrGrabberPositionTo( const CSSIntPoint& aClientPoint) { if (mIsResizing) { AutoEditActionDataSetter editActionData(*this, EditAction::eResizingElement); if (NS_WARN_IF(!editActionData.CanHandle())) { return NS_ERROR_NOT_INITIALIZED; } // we are resizing and the mouse pointer's position has changed // we have to resdisplay the shadow const int32_t newX = GetNewResizingX(aClientPoint.x, aClientPoint.y); const int32_t newY = GetNewResizingY(aClientPoint.x, aClientPoint.y); const int32_t newWidth = GetNewResizingWidth(aClientPoint.x, aClientPoint.y); const int32_t newHeight = GetNewResizingHeight(aClientPoint.x, aClientPoint.y); if (RefPtr resizingShadowStyledElement = nsStyledElement::FromNodeOrNull(mResizingShadow.get())) { nsresult rv; rv = CSSEditUtils::SetCSSPropertyPixelsWithoutTransaction( *resizingShadowStyledElement, *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"); rv = CSSEditUtils::SetCSSPropertyPixelsWithoutTransaction( *resizingShadowStyledElement, *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::SetCSSPropertyPixelsWithoutTransaction( *resizingShadowStyledElement, *nsGkAtoms::width, newWidth); if (rv == NS_ERROR_EDITOR_DESTROYED) { NS_WARNING( "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::" "width) destroyed the editor"); return NS_ERROR_EDITOR_DESTROYED; } NS_WARNING_ASSERTION( NS_SUCCEEDED(rv), "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::width) " "failed, but ignored"); rv = CSSEditUtils::SetCSSPropertyPixelsWithoutTransaction( *resizingShadowStyledElement, *nsGkAtoms::height, newHeight); if (rv == NS_ERROR_EDITOR_DESTROYED) { NS_WARNING( "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::" "height) destroyed the editor"); return NS_ERROR_EDITOR_DESTROYED; } NS_WARNING_ASSERTION( NS_SUCCEEDED(rv), "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::height)" " failed, but ignored"); } nsresult rv = SetResizingInfoPosition(newX, newY, newWidth, newHeight); NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "HTMLEditor::SetResizingInfoPosition() failed"); return rv; } AutoEditActionDataSetter editActionData(*this, EditAction::eMovingElement); if (NS_WARN_IF(!editActionData.CanHandle())) { return NS_ERROR_NOT_INITIALIZED; } if (mGrabberClicked) { int32_t xThreshold = LookAndFeel::GetInt(LookAndFeel::IntID::DragThresholdX, 1); int32_t yThreshold = LookAndFeel::GetInt(LookAndFeel::IntID::DragThresholdY, 1); if (DeprecatedAbs(aClientPoint.x - mOriginalX) * 2 >= xThreshold || DeprecatedAbs(aClientPoint.y - mOriginalY) * 2 >= yThreshold) { mGrabberClicked = false; DebugOnly rvIgnored = StartMoving(); NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored), "HTMLEditor::StartMoving() failed, but ignored"); } } if (mIsMoving) { int32_t newX = mPositionedObjectX + aClientPoint.x - mOriginalX; int32_t newY = mPositionedObjectY + aClientPoint.y - mOriginalY; // Maybe align newX and newY to the grid. SnapToGrid(newX, newY); if (RefPtr positioningShadowStyledElement = nsStyledElement::FromNodeOrNull(mPositioningShadow.get())) { nsresult rv; rv = CSSEditUtils::SetCSSPropertyPixelsWithoutTransaction( *positioningShadowStyledElement, *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"); rv = CSSEditUtils::SetCSSPropertyPixelsWithoutTransaction( *positioningShadowStyledElement, *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"); } } return NS_OK; } nsresult HTMLEditor::SetFinalSizeWithTransaction(int32_t aX, int32_t aY) { if (!mResizedObject) { // paranoia return NS_OK; } if (mActivatedHandle) { DebugOnly rvIgnored = mActivatedHandle->UnsetAttr( kNameSpaceID_None, nsGkAtoms::_moz_activated, true); NS_WARNING_ASSERTION( NS_SUCCEEDED(rvIgnored), "Element::UnsetAttr(nsGkAtoms::_moz_activated) failed, but ignored"); mActivatedHandle = nullptr; } // 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 left = GetNewResizingX(aX, aY); int32_t top = GetNewResizingY(aX, aY); int32_t width = GetNewResizingWidth(aX, aY); int32_t height = GetNewResizingHeight(aX, aY); bool setWidth = !mResizedObjectIsAbsolutelyPositioned || (width != mResizedObjectWidth); bool setHeight = !mResizedObjectIsAbsolutelyPositioned || (height != mResizedObjectHeight); int32_t x, y; x = left - ((mResizedObjectIsAbsolutelyPositioned) ? mResizedObjectBorderLeft + mResizedObjectMarginLeft : 0); y = top - ((mResizedObjectIsAbsolutelyPositioned) ? mResizedObjectBorderTop + mResizedObjectMarginTop : 0); // we want one transaction only from a user's point of view AutoPlaceholderBatch treatAsOneTransaction( *this, ScrollSelectionIntoView::Yes, __FUNCTION__); RefPtr resizedElement(mResizedObject); RefPtr resizedStyleElement = nsStyledElement::FromNodeOrNull(mResizedObject); if (mResizedObjectIsAbsolutelyPositioned && resizedStyleElement) { if (setHeight) { nsresult rv = CSSEditUtils::SetCSSPropertyPixelsWithTransaction( *this, *resizedStyleElement, *nsGkAtoms::top, y); if (rv == NS_ERROR_EDITOR_DESTROYED) { NS_WARNING( "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::" "top) destoyed the editor"); return NS_ERROR_EDITOR_DESTROYED; } NS_WARNING_ASSERTION( NS_SUCCEEDED(rv), "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::top) " "failed, but ignored"); } if (setWidth) { nsresult rv = CSSEditUtils::SetCSSPropertyPixelsWithTransaction( *this, *resizedStyleElement, *nsGkAtoms::left, x); if (rv == NS_ERROR_EDITOR_DESTROYED) { NS_WARNING( "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::" "left) destoyed the editor"); return NS_ERROR_EDITOR_DESTROYED; } NS_WARNING_ASSERTION( NS_SUCCEEDED(rv), "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::left) " "failed, but ignored"); } } if (IsCSSEnabled() || mResizedObjectIsAbsolutelyPositioned) { if (setWidth && resizedElement->HasAttr(kNameSpaceID_None, nsGkAtoms::width)) { nsresult rv = RemoveAttributeWithTransaction(*resizedElement, *nsGkAtoms::width); if (MOZ_UNLIKELY(rv == NS_ERROR_EDITOR_DESTROYED)) { NS_WARNING( "EditorBase::RemoveAttributeWithTransaction(nsGkAtoms::width) " "failed"); return NS_ERROR_EDITOR_DESTROYED; } NS_WARNING_ASSERTION( NS_SUCCEEDED(rv), "EditorBase::RemoveAttributeWithTransaction(nsGkAtoms::width) " "failed, but ignored"); } if (setHeight && resizedElement->HasAttr(kNameSpaceID_None, nsGkAtoms::height)) { nsresult rv = RemoveAttributeWithTransaction(*resizedElement, *nsGkAtoms::height); if (MOZ_UNLIKELY(rv == NS_ERROR_EDITOR_DESTROYED)) { NS_WARNING( "EditorBase::RemoveAttributeWithTransaction(nsGkAtoms::height) " "failed"); return NS_ERROR_EDITOR_DESTROYED; } NS_WARNING_ASSERTION( NS_SUCCEEDED(rv), "EditorBase::RemoveAttributeWithTransaction(nsGkAtoms::height) " "failed, but ignored"); } if (resizedStyleElement) { if (setWidth) { nsresult rv = CSSEditUtils::SetCSSPropertyPixelsWithTransaction( *this, *resizedStyleElement, *nsGkAtoms::width, width); if (NS_FAILED(rv)) { NS_WARNING( "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::" "width) destoyed the editor"); return NS_ERROR_EDITOR_DESTROYED; } NS_WARNING_ASSERTION( NS_SUCCEEDED(rv), "CSSEditUtils::SetCSSPropertyPixels(nsGkAtoms::width) " "failed, but ignored"); } if (setHeight) { nsresult rv = CSSEditUtils::SetCSSPropertyPixelsWithTransaction( *this, *resizedStyleElement, *nsGkAtoms::height, height); if (NS_FAILED(rv)) { NS_WARNING( "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::" "height) destoyed the editor"); return NS_ERROR_EDITOR_DESTROYED; } NS_WARNING_ASSERTION( NS_SUCCEEDED(rv), "CSSEditUtils::SetCSSPropertyPixels(nsGkAtoms::height) " "failed, but ignored"); } } } else { // we use HTML size and remove all equivalent CSS properties // we set the CSS width and height to remove it later, // triggering an immediate reflow; otherwise, we have problems // with asynchronous reflow if (resizedStyleElement) { if (setWidth) { nsresult rv = CSSEditUtils::SetCSSPropertyPixelsWithTransaction( *this, *resizedStyleElement, *nsGkAtoms::width, width); if (NS_FAILED(rv)) { NS_WARNING( "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::" "width) destoyed the editor"); return NS_ERROR_EDITOR_DESTROYED; } NS_WARNING_ASSERTION( NS_SUCCEEDED(rv), "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::" "width) failed, but ignored"); } if (setHeight) { nsresult rv = CSSEditUtils::SetCSSPropertyPixelsWithTransaction( *this, *resizedStyleElement, *nsGkAtoms::height, height); if (NS_FAILED(rv)) { NS_WARNING( "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::" "height) destoyed the editor"); return NS_ERROR_EDITOR_DESTROYED; } NS_WARNING_ASSERTION( NS_SUCCEEDED(rv), "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::" "height) failed, but ignored"); } } if (setWidth) { nsAutoString w; w.AppendInt(width); DebugOnly rvIgnored = SetAttributeWithTransaction(*resizedElement, *nsGkAtoms::width, w); if (NS_WARN_IF(Destroyed())) { return NS_ERROR_EDITOR_DESTROYED; } NS_WARNING_ASSERTION( NS_SUCCEEDED(rvIgnored), "EditorBase::SetAttributeWithTransaction(nsGkAtoms::width) " "failed, but ignored"); } if (setHeight) { nsAutoString h; h.AppendInt(height); DebugOnly rvIgnored = SetAttributeWithTransaction(*resizedElement, *nsGkAtoms::height, h); if (NS_WARN_IF(Destroyed())) { return NS_ERROR_EDITOR_DESTROYED; } NS_WARNING_ASSERTION( NS_SUCCEEDED(rvIgnored), "EditorBase::SetAttributeWithTransaction(nsGkAtoms::height) " "failed, but ignored"); } if (resizedStyleElement) { if (setWidth) { nsresult rv = CSSEditUtils::RemoveCSSPropertyWithTransaction( *this, *resizedStyleElement, *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"); } if (setHeight) { nsresult rv = CSSEditUtils::RemoveCSSPropertyWithTransaction( *this, *resizedStyleElement, *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"); } } } // keep track of that size mResizedObjectWidth = width; mResizedObjectHeight = height; nsresult rv = RefreshResizersInternal(); if (rv == NS_ERROR_EDITOR_DESTROYED) { return NS_ERROR_EDITOR_DESTROYED; } NS_WARNING_ASSERTION( NS_SUCCEEDED(rv), "HTMLEditor::RefreshResizersInternal() failed, but ignored"); return NS_OK; } NS_IMETHODIMP HTMLEditor::GetObjectResizingEnabled( bool* aIsObjectResizingEnabled) { *aIsObjectResizingEnabled = IsObjectResizerEnabled(); return NS_OK; } NS_IMETHODIMP HTMLEditor::SetObjectResizingEnabled( bool aObjectResizingEnabled) { EnableObjectResizer(aObjectResizingEnabled); return NS_OK; } #undef kTopLeft #undef kTop #undef kTopRight #undef kLeft #undef kRight #undef kBottomLeft #undef kBottom #undef kBottomRight } // namespace mozilla