tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

HTMLEditorObjectResizer.cpp (53900B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 #include "ErrorList.h"
      7 #include "HTMLEditor.h"
      8 
      9 #include "CSSEditUtils.h"
     10 #include "HTMLEditorEventListener.h"
     11 #include "HTMLEditUtils.h"
     12 
     13 #include "mozilla/DebugOnly.h"
     14 #include "mozilla/LookAndFeel.h"
     15 #include "mozilla/MathAlgorithms.h"
     16 #include "mozilla/mozalloc.h"
     17 #include "mozilla/Preferences.h"
     18 #include "mozilla/PresShell.h"
     19 #include "mozilla/StaticPrefs_editor.h"
     20 #include "mozilla/dom/Event.h"
     21 #include "mozilla/dom/MouseEvent.h"
     22 #include "mozilla/dom/EventTarget.h"
     23 #include "nsAtom.h"
     24 #include "nsAString.h"
     25 #include "nsCOMPtr.h"
     26 #include "nsDebug.h"
     27 #include "nsDOMTokenList.h"
     28 #include "nsError.h"
     29 #include "nsGkAtoms.h"
     30 #include "nsIContent.h"
     31 #include "nsID.h"
     32 #include "mozilla/dom/Document.h"
     33 #include "nsISupportsUtils.h"
     34 #include "nsPIDOMWindow.h"
     35 #include "nsReadableUtils.h"
     36 #include "nsString.h"
     37 #include "nsStringFwd.h"
     38 #include "nsStyledElement.h"
     39 #include "nsTextNode.h"
     40 #include "nscore.h"
     41 #include <algorithm>
     42 
     43 #define kTopLeft u"nw"_ns
     44 #define kTop u"n"_ns
     45 #define kTopRight u"ne"_ns
     46 #define kLeft u"w"_ns
     47 #define kRight u"e"_ns
     48 #define kBottomLeft u"sw"_ns
     49 #define kBottom u"s"_ns
     50 #define kBottomRight u"se"_ns
     51 
     52 namespace mozilla {
     53 
     54 using namespace dom;
     55 
     56 /******************************************************************************
     57 * mozilla::HTMLEditor
     58 ******************************************************************************/
     59 
     60 ManualNACPtr HTMLEditor::CreateResizer(int16_t aLocation,
     61                                       nsIContent& aParentContent) {
     62  ManualNACPtr resizer = CreateAnonymousElement(nsGkAtoms::span, aParentContent,
     63                                                u"mozResizer"_ns, false);
     64  if (!resizer) {
     65    NS_WARNING(
     66        "HTMLEditor::CreateAnonymousElement(nsGkAtoms::span, mozResizer) "
     67        "failed");
     68    return nullptr;
     69  }
     70 
     71  // add the mouse listener so we can detect a click on a resizer
     72  DebugOnly<nsresult> rvIgnored =
     73      resizer->AddEventListener(u"mousedown"_ns, mEventListener, true);
     74  NS_WARNING_ASSERTION(
     75      NS_SUCCEEDED(rvIgnored),
     76      "EventTarget::AddEventListener(mousedown) failed, but ignored");
     77  nsAutoString locationStr;
     78  switch (aLocation) {
     79    case nsIHTMLObjectResizer::eTopLeft:
     80      locationStr = kTopLeft;
     81      break;
     82    case nsIHTMLObjectResizer::eTop:
     83      locationStr = kTop;
     84      break;
     85    case nsIHTMLObjectResizer::eTopRight:
     86      locationStr = kTopRight;
     87      break;
     88 
     89    case nsIHTMLObjectResizer::eLeft:
     90      locationStr = kLeft;
     91      break;
     92    case nsIHTMLObjectResizer::eRight:
     93      locationStr = kRight;
     94      break;
     95 
     96    case nsIHTMLObjectResizer::eBottomLeft:
     97      locationStr = kBottomLeft;
     98      break;
     99    case nsIHTMLObjectResizer::eBottom:
    100      locationStr = kBottom;
    101      break;
    102    case nsIHTMLObjectResizer::eBottomRight:
    103      locationStr = kBottomRight;
    104      break;
    105  }
    106 
    107  if (NS_FAILED(resizer->SetAttr(kNameSpaceID_None, nsGkAtoms::anonlocation,
    108                                 locationStr, true))) {
    109    NS_WARNING("Element::SetAttr(nsGkAtoms::anonlocation) failed");
    110    return nullptr;
    111  }
    112  nsAutoString cursor = u"cursor: "_ns + locationStr + u"-resize"_ns;
    113  if (NS_FAILED(resizer->SetAttr(kNameSpaceID_None, nsGkAtoms::style, cursor,
    114                                 true))) {
    115    NS_WARNING("Element::SetAttr(nsGkAtoms::style) failed");
    116    return nullptr;
    117  }
    118  return resizer;
    119 }
    120 
    121 ManualNACPtr HTMLEditor::CreateShadow(nsIContent& aParentContent,
    122                                      Element& aOriginalObject) {
    123  // let's create an image through the element factory
    124  RefPtr<nsAtom> name;
    125  if (HTMLEditUtils::IsImageElement(aOriginalObject)) {
    126    name = nsGkAtoms::img;
    127  } else {
    128    name = nsGkAtoms::span;
    129  }
    130 
    131  return CreateAnonymousElement(name, aParentContent, u"mozResizingShadow"_ns,
    132                                true);
    133 }
    134 
    135 ManualNACPtr HTMLEditor::CreateResizingInfo(nsIContent& aParentContent) {
    136  // let's create an info box through the element factory
    137  return CreateAnonymousElement(nsGkAtoms::span, aParentContent,
    138                                u"mozResizingInfo"_ns, true);
    139 }
    140 
    141 nsresult HTMLEditor::SetAllResizersPosition() {
    142  if (NS_WARN_IF(!mTopLeftHandle)) {
    143    return NS_ERROR_FAILURE;  // There are no resizers.
    144  }
    145 
    146  int32_t x = mResizedObjectX;
    147  int32_t y = mResizedObjectY;
    148  int32_t w = mResizedObjectWidth;
    149  int32_t h = mResizedObjectHeight;
    150 
    151  nsAutoString value;
    152  float resizerWidth, resizerHeight;
    153  RefPtr<nsAtom> dummyUnit;
    154  DebugOnly<nsresult> rvIgnored = NS_OK;
    155  OwningNonNull<Element> topLeftHandle = *mTopLeftHandle.get();
    156  // XXX Do we really need to computed value rather than specified value?
    157  //     Because it's an anonymous node.
    158  rvIgnored = CSSEditUtils::GetComputedProperty(*topLeftHandle,
    159                                                *nsGkAtoms::width, value);
    160  if (NS_WARN_IF(Destroyed())) {
    161    return NS_ERROR_EDITOR_DESTROYED;
    162  }
    163  if (NS_WARN_IF(topLeftHandle != mTopLeftHandle)) {
    164    return NS_ERROR_FAILURE;
    165  }
    166  NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
    167                       "CSSEditUtils::GetComputedProperty(nsGkAtoms::width) "
    168                       "failed, but ignored");
    169  rvIgnored = CSSEditUtils::GetComputedProperty(*topLeftHandle,
    170                                                *nsGkAtoms::height, value);
    171  if (NS_WARN_IF(Destroyed())) {
    172    return NS_ERROR_EDITOR_DESTROYED;
    173  }
    174  if (NS_WARN_IF(topLeftHandle != mTopLeftHandle)) {
    175    return NS_ERROR_FAILURE;
    176  }
    177  NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
    178                       "CSSEditUtils::GetComputedProperty(nsGkAtoms::height) "
    179                       "failed, but ignored");
    180  CSSEditUtils::ParseLength(value, &resizerWidth, getter_AddRefs(dummyUnit));
    181  CSSEditUtils::ParseLength(value, &resizerHeight, getter_AddRefs(dummyUnit));
    182 
    183  int32_t rw = static_cast<int32_t>((resizerWidth + 1) / 2);
    184  int32_t rh = static_cast<int32_t>((resizerHeight + 1) / 2);
    185 
    186  // While moving each resizer, mutation event listener may hide the resizers.
    187  // And in worst case, new resizers may be recreated.  So, we need to store
    188  // all resizers here, and then, if we detect a resizer is removed or replaced,
    189  // we should do nothing anymore.
    190  // FYI: Note that only checking if mTopLeftHandle is replaced is enough.
    191  //      We're may be in hot path if user resizes an element a lot.  So,
    192  //      we should just add-ref mTopLeftHandle.
    193  auto setHandlePosition =
    194      [this](ManualNACPtr& aHandleElement, int32_t aNewX, int32_t aNewY)
    195          MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION -> nsresult {
    196    RefPtr<nsStyledElement> handleStyledElement =
    197        nsStyledElement::FromNodeOrNull(aHandleElement.get());
    198    if (!handleStyledElement) {
    199      return NS_OK;
    200    }
    201    nsresult rv = SetAnonymousElementPositionWithoutTransaction(
    202        *handleStyledElement, aNewX, aNewY);
    203    if (NS_FAILED(rv)) {
    204      NS_WARNING(
    205          "HTMLEditor::SetAnonymousElementPositionWithoutTransaction() "
    206          "failed");
    207      return rv;
    208    }
    209    return NS_WARN_IF(handleStyledElement != aHandleElement.get())
    210               ? NS_ERROR_FAILURE
    211               : NS_OK;
    212  };
    213  nsresult rv;
    214  rv = setHandlePosition(mTopLeftHandle, x - rw, y - rh);
    215  if (NS_FAILED(rv)) {
    216    NS_WARNING("Failed to set top-left handle position");
    217    return rv;
    218  }
    219  rv = setHandlePosition(mTopHandle, x + w / 2 - rw, y - rh);
    220  if (NS_FAILED(rv)) {
    221    NS_WARNING("Failed to set top handle position");
    222    return rv;
    223  }
    224  rv = setHandlePosition(mTopRightHandle, x + w - rw - 1, y - rh);
    225  if (NS_FAILED(rv)) {
    226    NS_WARNING("Failed to set top-right handle position");
    227    return rv;
    228  }
    229 
    230  rv = setHandlePosition(mLeftHandle, x - rw, y + h / 2 - rh);
    231  if (NS_FAILED(rv)) {
    232    NS_WARNING("Failed to set left handle position");
    233    return rv;
    234  }
    235  rv = setHandlePosition(mRightHandle, x + w - rw - 1, y + h / 2 - rh);
    236  if (NS_FAILED(rv)) {
    237    NS_WARNING("Failed to set right handle position");
    238    return rv;
    239  }
    240 
    241  rv = setHandlePosition(mBottomLeftHandle, x - rw, y + h - rh - 1);
    242  if (NS_FAILED(rv)) {
    243    NS_WARNING("Failed to set bottom-left handle position");
    244    return rv;
    245  }
    246  rv = setHandlePosition(mBottomHandle, x + w / 2 - rw, y + h - rh - 1);
    247  if (NS_FAILED(rv)) {
    248    NS_WARNING("Failed to set bottom handle position");
    249    return rv;
    250  }
    251  rv = setHandlePosition(mBottomRightHandle, x + w - rw - 1, y + h - rh - 1);
    252  if (NS_FAILED(rv)) {
    253    NS_WARNING("Failed to set bottom-right handle position");
    254    return rv;
    255  }
    256 
    257  return NS_OK;
    258 }
    259 
    260 nsresult HTMLEditor::RefreshResizers() {
    261  AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing);
    262  if (NS_WARN_IF(!editActionData.CanHandle())) {
    263    return NS_ERROR_NOT_INITIALIZED;
    264  }
    265 
    266  nsresult rv = RefreshResizersInternal();
    267  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
    268                       "HTMLEditor::RefreshResizersInternal() failed");
    269  return EditorBase::ToGenericNSResult(rv);
    270 }
    271 
    272 nsresult HTMLEditor::RefreshResizersInternal() {
    273  MOZ_ASSERT(IsEditActionDataAvailable());
    274 
    275  // Don't warn even if resizers are not visible since script cannot check
    276  // if they are visible and this is non-virtual method.  So, the cost of
    277  // calling this can be ignored.
    278  if (!mResizedObject) {
    279    return NS_OK;
    280  }
    281 
    282  OwningNonNull<Element> resizedObject = *mResizedObject;
    283  nsresult rv = GetPositionAndDimensions(
    284      resizedObject, mResizedObjectX, mResizedObjectY, mResizedObjectWidth,
    285      mResizedObjectHeight, mResizedObjectBorderLeft, mResizedObjectBorderTop,
    286      mResizedObjectMarginLeft, mResizedObjectMarginTop);
    287  if (NS_FAILED(rv)) {
    288    NS_WARNING("HTMLEditor::GetPositionAndDimensions() failed");
    289    return rv;
    290  }
    291  if (NS_WARN_IF(resizedObject != mResizedObject)) {
    292    return NS_ERROR_FAILURE;
    293  }
    294 
    295  rv = SetAllResizersPosition();
    296  if (NS_FAILED(rv)) {
    297    NS_WARNING("HTMLEditor::SetAllResizersPosition() failed");
    298    return rv;
    299  }
    300  if (NS_WARN_IF(resizedObject != mResizedObject)) {
    301    return NS_ERROR_FAILURE;
    302  }
    303 
    304  MOZ_ASSERT(
    305      mResizingShadow,
    306      "SetAllResizersPosition() should return error if resizers are hidden");
    307  RefPtr<Element> resizingShadow = mResizingShadow.get();
    308  rv = SetShadowPosition(*resizingShadow, resizedObject, mResizedObjectX,
    309                         mResizedObjectY);
    310  if (NS_FAILED(rv)) {
    311    NS_WARNING("HTMLEditor::SetShadowPosition() failed");
    312    return rv;
    313  }
    314  if (NS_WARN_IF(resizedObject != mResizedObject)) {
    315    return NS_ERROR_FAILURE;
    316  }
    317  return NS_OK;
    318 }
    319 
    320 nsresult HTMLEditor::ShowResizersInternal(Element& aResizedElement) {
    321  // When we have visible resizers, we cannot show new resizers.
    322  // So, the caller should call HideResizersInternal() first if this
    323  // returns error.
    324  if (NS_WARN_IF(mResizedObject)) {
    325    return NS_ERROR_UNEXPECTED;
    326  }
    327 
    328  nsCOMPtr<nsIContent> parentContent = aResizedElement.GetParent();
    329  if (NS_WARN_IF(!parentContent)) {
    330    return NS_ERROR_FAILURE;
    331  }
    332 
    333  const RefPtr<Element> editingHost = ComputeEditingHost();
    334  if (NS_WARN_IF(!editingHost) ||
    335      NS_WARN_IF(!aResizedElement.IsInclusiveDescendantOf(editingHost))) {
    336    return NS_ERROR_UNEXPECTED;
    337  }
    338 
    339  // Let's create and setup resizers.  If we failed something, we should
    340  // cancel everything which we do in this method.
    341  do {
    342    mResizedObject = &aResizedElement;
    343 
    344    // The resizers and the shadow will be anonymous siblings of the element.
    345    // Note that creating a resizer or shadow may causes calling
    346    // HideRisizersInternal() via a mutation event listener.  So, we should
    347    // store new resizer to a local variable, then, check:
    348    //   - whether creating resizer is already set to the member or not
    349    //   - whether resizing element is changed to another element
    350    // If showing resizers are canceled, we hit the latter check.
    351    // If resizers for another element is shown during this, we hit the latter
    352    // check too.
    353    // If resizers are just shown again for same element, we hit the former
    354    // check.
    355    ManualNACPtr newResizer =
    356        CreateResizer(nsIHTMLObjectResizer::eTopLeft, *parentContent);
    357    if (!newResizer) {
    358      NS_WARNING("HTMLEditor::CreateResizer(eTopLeft) failed");
    359      break;
    360    }
    361    if (NS_WARN_IF(mTopLeftHandle) ||
    362        NS_WARN_IF(mResizedObject != &aResizedElement)) {
    363      // Don't hide current resizers in this case because they are not what
    364      // we're creating.
    365      return NS_ERROR_FAILURE;
    366    }
    367    mTopLeftHandle = std::move(newResizer);
    368    newResizer = CreateResizer(nsIHTMLObjectResizer::eTop, *parentContent);
    369    if (!newResizer) {
    370      NS_WARNING("HTMLEditor::CreateResizer(eTop) failed");
    371      break;
    372    }
    373    if (NS_WARN_IF(mTopHandle) ||
    374        NS_WARN_IF(mResizedObject != &aResizedElement)) {
    375      return NS_ERROR_FAILURE;
    376    }
    377    mTopHandle = std::move(newResizer);
    378    newResizer = CreateResizer(nsIHTMLObjectResizer::eTopRight, *parentContent);
    379    if (!newResizer) {
    380      NS_WARNING("HTMLEditor::CreateResizer(eTopRight) failed");
    381      break;
    382    }
    383    if (NS_WARN_IF(mTopRightHandle) ||
    384        NS_WARN_IF(mResizedObject != &aResizedElement)) {
    385      return NS_ERROR_FAILURE;
    386    }
    387    mTopRightHandle = std::move(newResizer);
    388 
    389    newResizer = CreateResizer(nsIHTMLObjectResizer::eLeft, *parentContent);
    390    if (!newResizer) {
    391      NS_WARNING("HTMLEditor::CreateResizer(eLeft) failed");
    392      break;
    393    }
    394    if (NS_WARN_IF(mLeftHandle) ||
    395        NS_WARN_IF(mResizedObject != &aResizedElement)) {
    396      return NS_ERROR_FAILURE;
    397    }
    398    mLeftHandle = std::move(newResizer);
    399    newResizer = CreateResizer(nsIHTMLObjectResizer::eRight, *parentContent);
    400    if (!newResizer) {
    401      NS_WARNING("HTMLEditor::CreateResizer(eRight) failed");
    402      break;
    403    }
    404    if (NS_WARN_IF(mRightHandle) ||
    405        NS_WARN_IF(mResizedObject != &aResizedElement)) {
    406      return NS_ERROR_FAILURE;
    407    }
    408    mRightHandle = std::move(newResizer);
    409 
    410    newResizer =
    411        CreateResizer(nsIHTMLObjectResizer::eBottomLeft, *parentContent);
    412    if (!newResizer) {
    413      NS_WARNING("HTMLEditor::CreateResizer(eBottomLeft) failed");
    414      break;
    415    }
    416    if (NS_WARN_IF(mBottomLeftHandle) ||
    417        NS_WARN_IF(mResizedObject != &aResizedElement)) {
    418      return NS_ERROR_FAILURE;
    419    }
    420    mBottomLeftHandle = std::move(newResizer);
    421    newResizer = CreateResizer(nsIHTMLObjectResizer::eBottom, *parentContent);
    422    if (!newResizer) {
    423      NS_WARNING("HTMLEditor::CreateResizer(eBottom) failed");
    424      break;
    425    }
    426    if (NS_WARN_IF(mBottomHandle) ||
    427        NS_WARN_IF(mResizedObject != &aResizedElement)) {
    428      return NS_ERROR_FAILURE;
    429    }
    430    mBottomHandle = std::move(newResizer);
    431    newResizer =
    432        CreateResizer(nsIHTMLObjectResizer::eBottomRight, *parentContent);
    433    if (!newResizer) {
    434      NS_WARNING("HTMLEditor::CreateResizer(eBottomRight) failed");
    435      break;
    436    }
    437    if (NS_WARN_IF(mBottomRightHandle) ||
    438        NS_WARN_IF(mResizedObject != &aResizedElement)) {
    439      return NS_ERROR_FAILURE;
    440    }
    441    mBottomRightHandle = std::move(newResizer);
    442 
    443    // Store the last resizer which we created.  This is useful when we
    444    // need to check whether our resizers are hiddedn and recreated another
    445    // set of resizers or not.
    446    RefPtr<Element> createdBottomRightHandle = mBottomRightHandle.get();
    447 
    448    nsresult rv = GetPositionAndDimensions(
    449        aResizedElement, mResizedObjectX, mResizedObjectY, mResizedObjectWidth,
    450        mResizedObjectHeight, mResizedObjectBorderLeft, mResizedObjectBorderTop,
    451        mResizedObjectMarginLeft, mResizedObjectMarginTop);
    452    if (NS_FAILED(rv)) {
    453      NS_WARNING("HTMLEditor::GetPositionAndDimensions() failed");
    454      break;
    455    }
    456 
    457    // and let's set their absolute positions in the document
    458    rv = SetAllResizersPosition();
    459    if (NS_FAILED(rv)) {
    460      NS_WARNING("HTMLEditor::SetAllResizersPosition() failed");
    461      if (NS_WARN_IF(mBottomRightHandle.get() != createdBottomRightHandle)) {
    462        return NS_ERROR_FAILURE;
    463      }
    464      break;
    465    }
    466 
    467    // now, let's create the resizing shadow
    468    ManualNACPtr newShadow = CreateShadow(*parentContent, aResizedElement);
    469    if (!newShadow) {
    470      NS_WARNING("HTMLEditor::CreateShadow() failed");
    471      break;
    472    }
    473    if (NS_WARN_IF(mResizingShadow) ||
    474        NS_WARN_IF(mResizedObject != &aResizedElement)) {
    475      return NS_ERROR_FAILURE;
    476    }
    477    mResizingShadow = std::move(newShadow);
    478 
    479    // and set its position
    480    RefPtr<Element> resizingShadow = mResizingShadow.get();
    481    rv = SetShadowPosition(*resizingShadow, aResizedElement, mResizedObjectX,
    482                           mResizedObjectY);
    483    if (NS_FAILED(rv)) {
    484      NS_WARNING("HTMLEditor::SetShadowPosition() failed");
    485      if (NS_WARN_IF(mBottomRightHandle.get() != createdBottomRightHandle)) {
    486        return NS_ERROR_FAILURE;
    487      }
    488      break;
    489    }
    490 
    491    // and then the resizing info tooltip
    492    ManualNACPtr newResizingInfo = CreateResizingInfo(*parentContent);
    493    if (!newResizingInfo) {
    494      NS_WARNING("HTMLEditor::CreateResizingInfo() failed");
    495      break;
    496    }
    497    if (NS_WARN_IF(mResizingInfo) ||
    498        NS_WARN_IF(mResizedObject != &aResizedElement)) {
    499      return NS_ERROR_FAILURE;
    500    }
    501    mResizingInfo = std::move(newResizingInfo);
    502 
    503    // and listen to the "resize" event on the window first, get the
    504    // window from the document...
    505    if (NS_WARN_IF(!mEventListener)) {
    506      break;
    507    }
    508 
    509    rv = static_cast<HTMLEditorEventListener*>(mEventListener.get())
    510             ->ListenToWindowResizeEvent(true);
    511    if (NS_FAILED(rv)) {
    512      NS_WARNING(
    513          "HTMLEditorEventListener::ListenToWindowResizeEvent(true) failed");
    514      break;
    515    }
    516 
    517    MOZ_ASSERT(mResizedObject == &aResizedElement);
    518    return NS_OK;
    519  } while (true);
    520 
    521  DebugOnly<nsresult> rv = HideResizersInternal();
    522  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
    523                       "HTMLEditor::HideResizersInternal() failed to clean up");
    524  return NS_ERROR_FAILURE;
    525 }
    526 
    527 NS_IMETHODIMP HTMLEditor::HideResizers() {
    528  AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing);
    529  if (NS_WARN_IF(!editActionData.CanHandle())) {
    530    return NS_ERROR_NOT_INITIALIZED;
    531  }
    532 
    533  nsresult rv = HideResizersInternal();
    534  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
    535                       "HTMLEditor::HideResizersInternal() failed");
    536  return EditorBase::ToGenericNSResult(rv);
    537 }
    538 
    539 nsresult HTMLEditor::HideResizersInternal() {
    540  // Don't warn even if resizers are visible since script cannot check
    541  // if they are visible and this is non-virtual method.  So, the cost of
    542  // calling this can be ignored.
    543  if (!mResizedObject) {
    544    return NS_OK;
    545  }
    546 
    547  // get the presshell's document observer interface.
    548  RefPtr<PresShell> presShell = GetPresShell();
    549  NS_WARNING_ASSERTION(presShell, "There is no presShell");
    550  // We allow the pres shell to be null; when it is, we presume there
    551  // are no document observers to notify, but we still want to
    552  // UnbindFromTree.
    553 
    554  constexpr auto mousedown = u"mousedown"_ns;
    555 
    556  // HTMLEditor should forget all members related to resizers first since
    557  // removing a part of UI may cause showing the resizers again.  In such
    558  // case, the members may be overwritten by ShowResizers() and this will
    559  // lose the chance to release the old resizers.
    560  ManualNACPtr topLeftHandle(std::move(mTopLeftHandle));
    561  ManualNACPtr topHandle(std::move(mTopHandle));
    562  ManualNACPtr topRightHandle(std::move(mTopRightHandle));
    563  ManualNACPtr leftHandle(std::move(mLeftHandle));
    564  ManualNACPtr rightHandle(std::move(mRightHandle));
    565  ManualNACPtr bottomLeftHandle(std::move(mBottomLeftHandle));
    566  ManualNACPtr bottomHandle(std::move(mBottomHandle));
    567  ManualNACPtr bottomRightHandle(std::move(mBottomRightHandle));
    568  ManualNACPtr resizingShadow(std::move(mResizingShadow));
    569  ManualNACPtr resizingInfo(std::move(mResizingInfo));
    570  RefPtr<Element> activatedHandle(std::move(mActivatedHandle));
    571  RefPtr<Element> resizedObject(std::move(mResizedObject));
    572 
    573  // Remvoe all handles.
    574  RemoveListenerAndDeleteRef(mousedown, mEventListener, true,
    575                             std::move(topLeftHandle), presShell);
    576 
    577  RemoveListenerAndDeleteRef(mousedown, mEventListener, true,
    578                             std::move(topHandle), presShell);
    579 
    580  RemoveListenerAndDeleteRef(mousedown, mEventListener, true,
    581                             std::move(topRightHandle), presShell);
    582 
    583  RemoveListenerAndDeleteRef(mousedown, mEventListener, true,
    584                             std::move(leftHandle), presShell);
    585 
    586  RemoveListenerAndDeleteRef(mousedown, mEventListener, true,
    587                             std::move(rightHandle), presShell);
    588 
    589  RemoveListenerAndDeleteRef(mousedown, mEventListener, true,
    590                             std::move(bottomLeftHandle), presShell);
    591 
    592  RemoveListenerAndDeleteRef(mousedown, mEventListener, true,
    593                             std::move(bottomHandle), presShell);
    594 
    595  RemoveListenerAndDeleteRef(mousedown, mEventListener, true,
    596                             std::move(bottomRightHandle), presShell);
    597 
    598  RemoveListenerAndDeleteRef(mousedown, mEventListener, true,
    599                             std::move(resizingShadow), presShell);
    600 
    601  RemoveListenerAndDeleteRef(mousedown, mEventListener, true,
    602                             std::move(resizingInfo), presShell);
    603 
    604  // Remove active state of a resizer.
    605  if (activatedHandle) {
    606    activatedHandle->ClassList()->Remove(u"active"_ns, IgnoreErrors());
    607  }
    608 
    609  if (!mEventListener) {
    610    return NS_OK;
    611  }
    612 
    613  nsresult rv = static_cast<HTMLEditorEventListener*>(mEventListener.get())
    614                    ->ListenToMouseMoveEventForResizers(false);
    615  if (NS_FAILED(rv)) {
    616    NS_WARNING(
    617        "HTMLEditorEventListener::ListenToMouseMoveEventForResizers(false) "
    618        "failed");
    619    return rv;
    620  }
    621 
    622  // Remove resize event listener from the window.
    623  if (!mEventListener) {
    624    return NS_OK;
    625  }
    626 
    627  rv = static_cast<HTMLEditorEventListener*>(mEventListener.get())
    628           ->ListenToWindowResizeEvent(false);
    629  NS_WARNING_ASSERTION(
    630      NS_SUCCEEDED(rv),
    631      "HTMLEditorEventListener::ListenToWindowResizeEvent(false) failed");
    632  return rv;
    633 }
    634 
    635 void HTMLEditor::HideShadowAndInfo() {
    636  if (mResizingShadow) {
    637    DebugOnly<nsresult> rvIgnored = mResizingShadow->SetAttr(
    638        kNameSpaceID_None, nsGkAtoms::hidden, u""_ns, true);
    639    NS_WARNING_ASSERTION(
    640        NS_SUCCEEDED(rvIgnored),
    641        "Element::SetAttr(nsGkAtoms::hidden) failed, but ignored");
    642  }
    643  if (mResizingInfo) {
    644    DebugOnly<nsresult> rvIgnored = mResizingInfo->SetAttr(
    645        kNameSpaceID_None, nsGkAtoms::hidden, u""_ns, true);
    646    NS_WARNING_ASSERTION(
    647        NS_SUCCEEDED(rvIgnored),
    648        "Element::SetAttr(nsGkAtoms::hidden, hidden) failed, but ignored");
    649  }
    650 }
    651 
    652 nsresult HTMLEditor::StartResizing(Element& aHandleElement) {
    653  mIsResizing = true;
    654  mActivatedHandle = &aHandleElement;
    655  mActivatedHandle->ClassList()->Add(u"active"_ns, IgnoreErrors());
    656  if (NS_WARN_IF(Destroyed())) {
    657    return NS_ERROR_EDITOR_DESTROYED;
    658  }
    659 
    660  // do we want to preserve ratio or not?
    661  const bool preserveRatio =
    662      mResizedObject && HTMLEditUtils::IsImageElement(*mResizedObject);
    663 
    664  // the way we change the position/size of the shadow depends on
    665  // the handle
    666  nsAutoString locationStr;
    667  mActivatedHandle->GetAttr(nsGkAtoms::anonlocation, locationStr);
    668  if (locationStr.Equals(kTopLeft)) {
    669    SetResizeIncrements(1, 1, -1, -1, preserveRatio);
    670  } else if (locationStr.Equals(kTop)) {
    671    SetResizeIncrements(0, 1, 0, -1, false);
    672  } else if (locationStr.Equals(kTopRight)) {
    673    SetResizeIncrements(0, 1, 1, -1, preserveRatio);
    674  } else if (locationStr.Equals(kLeft)) {
    675    SetResizeIncrements(1, 0, -1, 0, false);
    676  } else if (locationStr.Equals(kRight)) {
    677    SetResizeIncrements(0, 0, 1, 0, false);
    678  } else if (locationStr.Equals(kBottomLeft)) {
    679    SetResizeIncrements(1, 0, -1, 1, preserveRatio);
    680  } else if (locationStr.Equals(kBottom)) {
    681    SetResizeIncrements(0, 0, 0, 1, false);
    682  } else if (locationStr.Equals(kBottomRight)) {
    683    SetResizeIncrements(0, 0, 1, 1, preserveRatio);
    684  }
    685 
    686  // make the shadow appear
    687  DebugOnly<nsresult> rvIgnored =
    688      mResizingShadow->UnsetAttr(kNameSpaceID_None, nsGkAtoms::hidden, true);
    689  if (NS_WARN_IF(Destroyed())) {
    690    return NS_ERROR_EDITOR_DESTROYED;
    691  }
    692  NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
    693                       "Element::UnsetAttr(nsGkAtoms::hidden) failed");
    694 
    695  // position it
    696  if (RefPtr<nsStyledElement> resizingShadowStyledElement =
    697          nsStyledElement::FromNodeOrNull(mResizingShadow.get())) {
    698    nsresult rv;
    699    rv = CSSEditUtils::SetCSSPropertyPixelsWithoutTransaction(
    700        *this, *resizingShadowStyledElement, *nsGkAtoms::width,
    701        mResizedObjectWidth);
    702    if (rv == NS_ERROR_EDITOR_DESTROYED) {
    703      NS_WARNING(
    704          "CSSEditUtils::SetCSSPropertyPixelsWithoutTransaction("
    705          "nsGkAtoms::width) destroyed the editor");
    706      return NS_ERROR_EDITOR_DESTROYED;
    707    }
    708    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
    709                         "CSSEditUtils::SetCSSPropertyPixelsWithoutTransaction("
    710                         "nsGkAtoms::width) failed");
    711    rv = CSSEditUtils::SetCSSPropertyPixelsWithoutTransaction(
    712        *this, *resizingShadowStyledElement, *nsGkAtoms::height,
    713        mResizedObjectHeight);
    714    if (rv == NS_ERROR_EDITOR_DESTROYED) {
    715      NS_WARNING(
    716          "CSSEditUtils::SetCSSPropertyPixelsWithoutTransaction("
    717          "nsGkAtoms::height) destroyed the editor");
    718      return NS_ERROR_EDITOR_DESTROYED;
    719    }
    720    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
    721                         "CSSEditUtils::SetCSSPropertyPixelsWithoutTransaction("
    722                         "nsGkAtoms::height) failed");
    723  }
    724 
    725  // add a mouse move listener to the editor
    726  if (NS_WARN_IF(!mEventListener)) {
    727    return NS_ERROR_NOT_INITIALIZED;
    728  }
    729  nsresult rv = static_cast<HTMLEditorEventListener*>(mEventListener.get())
    730                    ->ListenToMouseMoveEventForResizers(true);
    731  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
    732                       "HTMLEditorEventListener::"
    733                       "ListenToMouseMoveEventForResizers(true) failed");
    734  return rv;
    735 }
    736 
    737 nsresult HTMLEditor::StartToDragResizerOrHandleDragGestureOnGrabber(
    738    MouseEvent& aMouseDownEvent, Element& aEventTargetElement) {
    739  MOZ_ASSERT(aMouseDownEvent.GetExplicitOriginalTarget() ==
    740             &aEventTargetElement);
    741  MOZ_ASSERT(!aMouseDownEvent.DefaultPrevented());
    742  MOZ_ASSERT(aMouseDownEvent.WidgetEventPtr()->mMessage == eMouseDown);
    743 
    744  nsDOMTokenList& classList = *aEventTargetElement.ClassList();
    745 
    746  if (classList.Contains(u"mozResizer"_ns)) {
    747    AutoEditActionDataSetter editActionData(*this,
    748                                            EditAction::eResizingElement);
    749    if (NS_WARN_IF(!editActionData.CanHandle())) {
    750      return NS_ERROR_NOT_INITIALIZED;
    751    }
    752 
    753    // If we have an anonymous element and that element is a resizer,
    754    // let's start resizing!
    755    aMouseDownEvent.PreventDefault();
    756    mOriginalX = aMouseDownEvent.ClientX();
    757    mOriginalY = aMouseDownEvent.ClientY();
    758    nsresult rv = StartResizing(aEventTargetElement);
    759    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
    760                         "HTMLEditor::StartResizing() failed");
    761    return EditorBase::ToGenericNSResult(rv);
    762  }
    763 
    764  if (classList.Contains(u"mozGrabber"_ns)) {
    765    AutoEditActionDataSetter editActionData(*this, EditAction::eMovingElement);
    766    if (NS_WARN_IF(!editActionData.CanHandle())) {
    767      return NS_ERROR_NOT_INITIALIZED;
    768    }
    769 
    770    // If we have an anonymous element and that element is a grabber,
    771    // let's start moving the element!
    772    mOriginalX = aMouseDownEvent.ClientX();
    773    mOriginalY = aMouseDownEvent.ClientY();
    774    nsresult rv = GrabberClicked();
    775    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
    776                         "HTMLEditor::GrabberClicked() failed");
    777    return EditorBase::ToGenericNSResult(rv);
    778  }
    779 
    780  return NS_OK;
    781 }
    782 
    783 nsresult HTMLEditor::StopDraggingResizerOrGrabberAt(
    784    const CSSIntPoint& aClientPoint) {
    785  if (mIsResizing) {
    786    AutoEditActionDataSetter editActionData(*this, EditAction::eResizeElement);
    787    if (NS_WARN_IF(!editActionData.CanHandle())) {
    788      return NS_ERROR_NOT_INITIALIZED;
    789    }
    790 
    791    // we are resizing and release the mouse button, so let's
    792    // end the resizing process
    793    mIsResizing = false;
    794    HideShadowAndInfo();
    795 
    796    nsresult rv = editActionData.MaybeDispatchBeforeInputEvent();
    797    if (NS_FAILED(rv)) {
    798      NS_WARNING_ASSERTION(
    799          rv == NS_ERROR_EDITOR_ACTION_CANCELED,
    800          "EditorBase::MaybeDispatchBeforeInputEvent(), failed");
    801      return EditorBase::ToGenericNSResult(rv);
    802    }
    803 
    804    rv = SetFinalSizeWithTransaction(aClientPoint.x, aClientPoint.y);
    805    if (rv == NS_ERROR_EDITOR_DESTROYED) {
    806      NS_WARNING(
    807          "HTMLEditor::SetFinalSizeWithTransaction() destroyed the editor");
    808      return NS_ERROR_EDITOR_DESTROYED;
    809    }
    810    NS_WARNING_ASSERTION(
    811        NS_SUCCEEDED(rv),
    812        "HTMLEditor::SetFinalSizeWithTransaction() failed, but ignored");
    813    return NS_OK;
    814  }
    815 
    816  if (mIsMoving || mGrabberClicked) {
    817    AutoEditActionDataSetter editActionData(*this, EditAction::eMoveElement);
    818    nsresult rv = editActionData.CanHandleAndMaybeDispatchBeforeInputEvent();
    819    if (rv != NS_ERROR_EDITOR_ACTION_CANCELED && NS_WARN_IF(NS_FAILED(rv))) {
    820      NS_WARNING("CanHandleAndMaybeDispatchBeforeInputEvent() failed");
    821      return EditorBase::ToGenericNSResult(rv);
    822    }
    823 
    824    if (mIsMoving) {
    825      DebugOnly<nsresult> rvIgnored = mPositioningShadow->SetAttr(
    826          kNameSpaceID_None, nsGkAtoms::hidden, u""_ns, true);
    827      NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
    828                           "Element::SetAttr(nsGkAtoms::hidden) failed");
    829      if (rv != NS_ERROR_EDITOR_ACTION_CANCELED) {
    830        SetFinalPosition(aClientPoint.x, aClientPoint.y);
    831      }
    832    }
    833    if (mGrabberClicked) {
    834      DebugOnly<nsresult> rvIgnored = EndMoving();
    835      NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
    836                           "HTMLEditor::EndMoving() failed");
    837    }
    838    return EditorBase::ToGenericNSResult(rv);
    839  }
    840 
    841  return NS_OK;
    842 }
    843 
    844 void HTMLEditor::SetResizeIncrements(int32_t aX, int32_t aY, int32_t aW,
    845                                     int32_t aH, bool aPreserveRatio) {
    846  mXIncrementFactor = aX;
    847  mYIncrementFactor = aY;
    848  mWidthIncrementFactor = aW;
    849  mHeightIncrementFactor = aH;
    850  mPreserveRatio = aPreserveRatio;
    851 }
    852 
    853 nsresult HTMLEditor::SetResizingInfoPosition(int32_t aX, int32_t aY, int32_t aW,
    854                                             int32_t aH) {
    855  // Determine the position of the resizing info box based upon the new
    856  // position and size of the element (aX, aY, aW, aH), and which
    857  // resizer is the "activated handle".  For example, place the resizing
    858  // info box at the bottom-right corner of the new element, if the element
    859  // is being resized by the bottom-right resizer.
    860  int32_t infoXPosition;
    861  int32_t infoYPosition;
    862 
    863  if (mActivatedHandle == mTopLeftHandle || mActivatedHandle == mLeftHandle ||
    864      mActivatedHandle == mBottomLeftHandle) {
    865    infoXPosition = aX;
    866  } else if (mActivatedHandle == mTopHandle ||
    867             mActivatedHandle == mBottomHandle) {
    868    infoXPosition = aX + (aW / 2);
    869  } else {
    870    // should only occur when mActivatedHandle is one of the 3 right-side
    871    // handles, but this is a reasonable default if it isn't any of them (?)
    872    infoXPosition = aX + aW;
    873  }
    874 
    875  if (mActivatedHandle == mTopLeftHandle || mActivatedHandle == mTopHandle ||
    876      mActivatedHandle == mTopRightHandle) {
    877    infoYPosition = aY;
    878  } else if (mActivatedHandle == mLeftHandle ||
    879             mActivatedHandle == mRightHandle) {
    880    infoYPosition = aY + (aH / 2);
    881  } else {
    882    // should only occur when mActivatedHandle is one of the 3 bottom-side
    883    // handles, but this is a reasonable default if it isn't any of them (?)
    884    infoYPosition = aY + aH;
    885  }
    886 
    887  // Offset info box by 20 so it's not directly under the mouse cursor.
    888  const int mouseCursorOffset = 20;
    889  if (RefPtr<nsStyledElement> resizingInfoStyledElement =
    890          nsStyledElement::FromNodeOrNull(mResizingInfo.get())) {
    891    nsresult rv;
    892    rv = CSSEditUtils::SetCSSPropertyPixelsWithoutTransaction(
    893        *this, *resizingInfoStyledElement, *nsGkAtoms::left,
    894        infoXPosition + mouseCursorOffset);
    895    if (rv == NS_ERROR_EDITOR_DESTROYED) {
    896      NS_WARNING(
    897          "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::left) "
    898          "destroyed the editor");
    899      return NS_ERROR_EDITOR_DESTROYED;
    900    }
    901    NS_WARNING_ASSERTION(
    902        NS_SUCCEEDED(rv),
    903        "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::left) "
    904        "failed, but ignored");
    905    rv = CSSEditUtils::SetCSSPropertyPixelsWithoutTransaction(
    906        *this, *resizingInfoStyledElement, *nsGkAtoms::top,
    907        infoYPosition + mouseCursorOffset);
    908    if (rv == NS_ERROR_EDITOR_DESTROYED) {
    909      NS_WARNING(
    910          "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::top) "
    911          "destroyed the editor");
    912      return NS_ERROR_EDITOR_DESTROYED;
    913    }
    914    NS_WARNING_ASSERTION(
    915        NS_SUCCEEDED(rv),
    916        "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::top) "
    917        "failed, but ignored");
    918  }
    919 
    920  nsCOMPtr<nsIContent> textInfo = mResizingInfo->GetFirstChild();
    921  ErrorResult error;
    922  if (textInfo) {
    923    mResizingInfo->RemoveChild(*textInfo, error);
    924    if (error.Failed()) {
    925      NS_WARNING("nsINode::RemoveChild() failed");
    926      return error.StealNSResult();
    927    }
    928    textInfo = nullptr;
    929  }
    930 
    931  nsAutoString widthStr, heightStr, diffWidthStr, diffHeightStr;
    932  widthStr.AppendInt(aW);
    933  heightStr.AppendInt(aH);
    934  int32_t diffWidth = aW - mResizedObjectWidth;
    935  int32_t diffHeight = aH - mResizedObjectHeight;
    936  if (diffWidth > 0) {
    937    diffWidthStr.Assign('+');
    938  }
    939  if (diffHeight > 0) {
    940    diffHeightStr.Assign('+');
    941  }
    942  diffWidthStr.AppendInt(diffWidth);
    943  diffHeightStr.AppendInt(diffHeight);
    944 
    945  nsAutoString info(widthStr + u" x "_ns + heightStr + u" ("_ns + diffWidthStr +
    946                    u", "_ns + diffHeightStr + u")"_ns);
    947 
    948  RefPtr<Document> document = GetDocument();
    949  textInfo = document->CreateTextNode(info);
    950  if (!textInfo) {
    951    NS_WARNING("Document::CreateTextNode() failed");
    952    return NS_ERROR_FAILURE;
    953  }
    954  mResizingInfo->AppendChild(*textInfo, error);
    955  if (error.Failed()) {
    956    NS_WARNING("nsINode::AppendChild() failed");
    957    return error.StealNSResult();
    958  }
    959 
    960  nsresult rv =
    961      mResizingInfo->UnsetAttr(kNameSpaceID_None, nsGkAtoms::hidden, true);
    962  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
    963                       "Element::UnsetAttr(nsGkAtoms::hidden) failed");
    964  return rv;
    965 }
    966 
    967 nsresult HTMLEditor::SetShadowPosition(Element& aShadowElement,
    968                                       Element& aElement, int32_t aElementX,
    969                                       int32_t aElementY) {
    970  MOZ_ASSERT(&aShadowElement == mResizingShadow ||
    971             &aShadowElement == mPositioningShadow);
    972  RefPtr<Element> handlingShadowElement = &aShadowElement == mResizingShadow
    973                                              ? mResizingShadow.get()
    974                                              : mPositioningShadow.get();
    975 
    976  if (nsStyledElement* styledShadowElement =
    977          nsStyledElement::FromNode(&aShadowElement)) {
    978    // MOZ_KnownLive(*styledShadowElement): It's aShadowElement whose lifetime
    979    // must be guaranteed by caller because of MOZ_CAN_RUN_SCRIPT method.
    980    nsresult rv = SetAnonymousElementPositionWithoutTransaction(
    981        MOZ_KnownLive(*styledShadowElement), aElementX, aElementY);
    982    if (NS_FAILED(rv)) {
    983      NS_WARNING(
    984          "HTMLEditor::SetAnonymousElementPositionWithoutTransaction() "
    985          "failed");
    986      return rv;
    987    }
    988  }
    989 
    990  if (!HTMLEditUtils::IsImageElement(aElement)) {
    991    return NS_OK;
    992  }
    993 
    994  nsAutoString imageSource;
    995  aElement.GetAttr(nsGkAtoms::src, imageSource);
    996  nsresult rv = aShadowElement.SetAttr(kNameSpaceID_None, nsGkAtoms::src,
    997                                       imageSource, true);
    998  if (NS_WARN_IF(Destroyed())) {
    999    return NS_ERROR_EDITOR_DESTROYED;
   1000  }
   1001  if (NS_FAILED(rv)) {
   1002    NS_WARNING("Element::SetAttr(nsGkAtoms::src) failed");
   1003    return NS_ERROR_FAILURE;
   1004  }
   1005  return NS_WARN_IF(&aShadowElement != handlingShadowElement) ? NS_ERROR_FAILURE
   1006                                                              : NS_OK;
   1007 }
   1008 
   1009 int32_t HTMLEditor::GetNewResizingIncrement(int32_t aX, int32_t aY,
   1010                                            ResizeAt aResizeAt) const {
   1011  int32_t result = 0;
   1012  if (!mPreserveRatio) {
   1013    switch (aResizeAt) {
   1014      case ResizeAt::eX:
   1015      case ResizeAt::eWidth:
   1016        result = aX - mOriginalX;
   1017        break;
   1018      case ResizeAt::eY:
   1019      case ResizeAt::eHeight:
   1020        result = aY - mOriginalY;
   1021        break;
   1022      default:
   1023        MOZ_ASSERT_UNREACHABLE("Invalid resizing request");
   1024    }
   1025    return result;
   1026  }
   1027 
   1028  int32_t xi = (aX - mOriginalX) * mWidthIncrementFactor;
   1029  int32_t yi = (aY - mOriginalY) * mHeightIncrementFactor;
   1030  float objectSizeRatio =
   1031      ((float)mResizedObjectWidth) / ((float)mResizedObjectHeight);
   1032  result = (xi > yi) ? xi : yi;
   1033  switch (aResizeAt) {
   1034    case ResizeAt::eX:
   1035    case ResizeAt::eWidth:
   1036      if (result == yi) result = (int32_t)(((float)result) * objectSizeRatio);
   1037      result = (int32_t)(((float)result) * mWidthIncrementFactor);
   1038      break;
   1039    case ResizeAt::eY:
   1040    case ResizeAt::eHeight:
   1041      if (result == xi) result = (int32_t)(((float)result) / objectSizeRatio);
   1042      result = (int32_t)(((float)result) * mHeightIncrementFactor);
   1043      break;
   1044  }
   1045  return result;
   1046 }
   1047 
   1048 int32_t HTMLEditor::GetNewResizingX(int32_t aX, int32_t aY) {
   1049  int32_t resized =
   1050      mResizedObjectX +
   1051      GetNewResizingIncrement(aX, aY, ResizeAt::eX) * mXIncrementFactor;
   1052  int32_t max = mResizedObjectX + mResizedObjectWidth;
   1053  return std::min(resized, max);
   1054 }
   1055 
   1056 int32_t HTMLEditor::GetNewResizingY(int32_t aX, int32_t aY) {
   1057  int32_t resized =
   1058      mResizedObjectY +
   1059      GetNewResizingIncrement(aX, aY, ResizeAt::eY) * mYIncrementFactor;
   1060  int32_t max = mResizedObjectY + mResizedObjectHeight;
   1061  return std::min(resized, max);
   1062 }
   1063 
   1064 int32_t HTMLEditor::GetNewResizingWidth(int32_t aX, int32_t aY) {
   1065  int32_t resized =
   1066      mResizedObjectWidth +
   1067      GetNewResizingIncrement(aX, aY, ResizeAt::eWidth) * mWidthIncrementFactor;
   1068  return std::max(resized, 1);
   1069 }
   1070 
   1071 int32_t HTMLEditor::GetNewResizingHeight(int32_t aX, int32_t aY) {
   1072  int32_t resized = mResizedObjectHeight +
   1073                    GetNewResizingIncrement(aX, aY, ResizeAt::eHeight) *
   1074                        mHeightIncrementFactor;
   1075  return std::max(resized, 1);
   1076 }
   1077 
   1078 nsresult HTMLEditor::UpdateResizerOrGrabberPositionTo(
   1079    const CSSIntPoint& aClientPoint) {
   1080  if (mIsResizing) {
   1081    AutoEditActionDataSetter editActionData(*this,
   1082                                            EditAction::eResizingElement);
   1083    if (NS_WARN_IF(!editActionData.CanHandle())) {
   1084      return NS_ERROR_NOT_INITIALIZED;
   1085    }
   1086 
   1087    // we are resizing and the mouse pointer's position has changed
   1088    // we have to resdisplay the shadow
   1089    const int32_t newX = GetNewResizingX(aClientPoint.x, aClientPoint.y);
   1090    const int32_t newY = GetNewResizingY(aClientPoint.x, aClientPoint.y);
   1091    const int32_t newWidth =
   1092        GetNewResizingWidth(aClientPoint.x, aClientPoint.y);
   1093    const int32_t newHeight =
   1094        GetNewResizingHeight(aClientPoint.x, aClientPoint.y);
   1095 
   1096    if (RefPtr<nsStyledElement> resizingShadowStyledElement =
   1097            nsStyledElement::FromNodeOrNull(mResizingShadow.get())) {
   1098      nsresult rv;
   1099      rv = CSSEditUtils::SetCSSPropertyPixelsWithoutTransaction(
   1100          *this, *resizingShadowStyledElement, *nsGkAtoms::left, newX);
   1101      if (rv == NS_ERROR_EDITOR_DESTROYED) {
   1102        NS_WARNING(
   1103            "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::left)"
   1104            " destroyed the editor");
   1105        return NS_ERROR_EDITOR_DESTROYED;
   1106      }
   1107      NS_WARNING_ASSERTION(
   1108          NS_SUCCEEDED(rv),
   1109          "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::left) "
   1110          "failed, but ignored");
   1111      rv = CSSEditUtils::SetCSSPropertyPixelsWithoutTransaction(
   1112          *this, *resizingShadowStyledElement, *nsGkAtoms::top, newY);
   1113      if (rv == NS_ERROR_EDITOR_DESTROYED) {
   1114        NS_WARNING(
   1115            "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::top)"
   1116            " destroyed the editor");
   1117        return NS_ERROR_EDITOR_DESTROYED;
   1118      }
   1119      NS_WARNING_ASSERTION(
   1120          NS_SUCCEEDED(rv),
   1121          "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::top) "
   1122          "failed, but ignored");
   1123      rv = CSSEditUtils::SetCSSPropertyPixelsWithoutTransaction(
   1124          *this, *resizingShadowStyledElement, *nsGkAtoms::width, newWidth);
   1125      if (rv == NS_ERROR_EDITOR_DESTROYED) {
   1126        NS_WARNING(
   1127            "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::"
   1128            "width) destroyed the editor");
   1129        return NS_ERROR_EDITOR_DESTROYED;
   1130      }
   1131      NS_WARNING_ASSERTION(
   1132          NS_SUCCEEDED(rv),
   1133          "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::width) "
   1134          "failed, but ignored");
   1135      rv = CSSEditUtils::SetCSSPropertyPixelsWithoutTransaction(
   1136          *this, *resizingShadowStyledElement, *nsGkAtoms::height, newHeight);
   1137      if (rv == NS_ERROR_EDITOR_DESTROYED) {
   1138        NS_WARNING(
   1139            "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::"
   1140            "height) destroyed the editor");
   1141        return NS_ERROR_EDITOR_DESTROYED;
   1142      }
   1143      NS_WARNING_ASSERTION(
   1144          NS_SUCCEEDED(rv),
   1145          "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::height)"
   1146          " failed, but ignored");
   1147    }
   1148 
   1149    nsresult rv = SetResizingInfoPosition(newX, newY, newWidth, newHeight);
   1150    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   1151                         "HTMLEditor::SetResizingInfoPosition() failed");
   1152    return rv;
   1153  }
   1154 
   1155  AutoEditActionDataSetter editActionData(*this, EditAction::eMovingElement);
   1156  if (NS_WARN_IF(!editActionData.CanHandle())) {
   1157    return NS_ERROR_NOT_INITIALIZED;
   1158  }
   1159 
   1160  if (mGrabberClicked) {
   1161    int32_t xThreshold =
   1162        LookAndFeel::GetInt(LookAndFeel::IntID::DragThresholdX, 1);
   1163    int32_t yThreshold =
   1164        LookAndFeel::GetInt(LookAndFeel::IntID::DragThresholdY, 1);
   1165 
   1166    if (DeprecatedAbs(aClientPoint.x - mOriginalX) * 2 >= xThreshold ||
   1167        DeprecatedAbs(aClientPoint.y - mOriginalY) * 2 >= yThreshold) {
   1168      mGrabberClicked = false;
   1169      DebugOnly<nsresult> rvIgnored = StartMoving();
   1170      NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
   1171                           "HTMLEditor::StartMoving() failed, but ignored");
   1172    }
   1173  }
   1174  if (mIsMoving) {
   1175    int32_t newX = mPositionedObjectX + aClientPoint.x - mOriginalX;
   1176    int32_t newY = mPositionedObjectY + aClientPoint.y - mOriginalY;
   1177 
   1178    // Maybe align newX and newY to the grid.
   1179    SnapToGrid(newX, newY);
   1180 
   1181    if (RefPtr<nsStyledElement> positioningShadowStyledElement =
   1182            nsStyledElement::FromNodeOrNull(mPositioningShadow.get())) {
   1183      nsresult rv;
   1184      rv = CSSEditUtils::SetCSSPropertyPixelsWithoutTransaction(
   1185          *this, *positioningShadowStyledElement, *nsGkAtoms::left, newX);
   1186      if (rv == NS_ERROR_EDITOR_DESTROYED) {
   1187        NS_WARNING(
   1188            "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::left)"
   1189            " destroyed the editor");
   1190        return NS_ERROR_EDITOR_DESTROYED;
   1191      }
   1192      NS_WARNING_ASSERTION(
   1193          NS_SUCCEEDED(rv),
   1194          "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::left) "
   1195          "failed, but ignored");
   1196      rv = CSSEditUtils::SetCSSPropertyPixelsWithoutTransaction(
   1197          *this, *positioningShadowStyledElement, *nsGkAtoms::top, newY);
   1198      if (rv == NS_ERROR_EDITOR_DESTROYED) {
   1199        NS_WARNING(
   1200            "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::top) "
   1201            "destroyed the editor");
   1202        return NS_ERROR_EDITOR_DESTROYED;
   1203      }
   1204      NS_WARNING_ASSERTION(
   1205          NS_SUCCEEDED(rv),
   1206          "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::top) "
   1207          "failed, but ignored");
   1208    }
   1209  }
   1210  return NS_OK;
   1211 }
   1212 
   1213 nsresult HTMLEditor::SetFinalSizeWithTransaction(int32_t aX, int32_t aY) {
   1214  if (!mResizedObject) {
   1215    // paranoia
   1216    return NS_OK;
   1217  }
   1218 
   1219  if (mActivatedHandle) {
   1220    mActivatedHandle->ClassList()->Remove(u"active"_ns, IgnoreErrors());
   1221    mActivatedHandle = nullptr;
   1222  }
   1223 
   1224  // we have now to set the new width and height of the resized object
   1225  // we don't set the x and y position because we don't control that in
   1226  // a normal HTML layout
   1227  int32_t left = GetNewResizingX(aX, aY);
   1228  int32_t top = GetNewResizingY(aX, aY);
   1229  int32_t width = GetNewResizingWidth(aX, aY);
   1230  int32_t height = GetNewResizingHeight(aX, aY);
   1231  bool setWidth =
   1232      !mResizedObjectIsAbsolutelyPositioned || (width != mResizedObjectWidth);
   1233  bool setHeight =
   1234      !mResizedObjectIsAbsolutelyPositioned || (height != mResizedObjectHeight);
   1235 
   1236  int32_t x, y;
   1237  x = left - ((mResizedObjectIsAbsolutelyPositioned)
   1238                  ? mResizedObjectBorderLeft + mResizedObjectMarginLeft
   1239                  : 0);
   1240  y = top - ((mResizedObjectIsAbsolutelyPositioned)
   1241                 ? mResizedObjectBorderTop + mResizedObjectMarginTop
   1242                 : 0);
   1243 
   1244  // we want one transaction only from a user's point of view
   1245  AutoPlaceholderBatch treatAsOneTransaction(
   1246      *this, ScrollSelectionIntoView::Yes, __FUNCTION__);
   1247  RefPtr<Element> resizedElement(mResizedObject);
   1248  RefPtr<nsStyledElement> resizedStyleElement =
   1249      nsStyledElement::FromNodeOrNull(mResizedObject);
   1250 
   1251  if (mResizedObjectIsAbsolutelyPositioned && resizedStyleElement) {
   1252    if (setHeight) {
   1253      nsresult rv = CSSEditUtils::SetCSSPropertyPixelsWithTransaction(
   1254          *this, *resizedStyleElement, *nsGkAtoms::top, y);
   1255      if (rv == NS_ERROR_EDITOR_DESTROYED) {
   1256        NS_WARNING(
   1257            "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::"
   1258            "top) destoyed the editor");
   1259        return NS_ERROR_EDITOR_DESTROYED;
   1260      }
   1261      NS_WARNING_ASSERTION(
   1262          NS_SUCCEEDED(rv),
   1263          "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::top) "
   1264          "failed, but ignored");
   1265    }
   1266    if (setWidth) {
   1267      nsresult rv = CSSEditUtils::SetCSSPropertyPixelsWithTransaction(
   1268          *this, *resizedStyleElement, *nsGkAtoms::left, x);
   1269      if (rv == NS_ERROR_EDITOR_DESTROYED) {
   1270        NS_WARNING(
   1271            "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::"
   1272            "left) destoyed the editor");
   1273        return NS_ERROR_EDITOR_DESTROYED;
   1274      }
   1275      NS_WARNING_ASSERTION(
   1276          NS_SUCCEEDED(rv),
   1277          "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::left) "
   1278          "failed, but ignored");
   1279    }
   1280  }
   1281  if (IsCSSEnabled() || mResizedObjectIsAbsolutelyPositioned) {
   1282    if (setWidth && resizedElement->HasAttr(nsGkAtoms::width)) {
   1283      nsresult rv =
   1284          RemoveAttributeWithTransaction(*resizedElement, *nsGkAtoms::width);
   1285      if (MOZ_UNLIKELY(rv == NS_ERROR_EDITOR_DESTROYED)) {
   1286        NS_WARNING(
   1287            "EditorBase::RemoveAttributeWithTransaction(nsGkAtoms::width) "
   1288            "failed");
   1289        return NS_ERROR_EDITOR_DESTROYED;
   1290      }
   1291      NS_WARNING_ASSERTION(
   1292          NS_SUCCEEDED(rv),
   1293          "EditorBase::RemoveAttributeWithTransaction(nsGkAtoms::width) "
   1294          "failed, but ignored");
   1295    }
   1296 
   1297    if (setHeight && resizedElement->HasAttr(nsGkAtoms::height)) {
   1298      nsresult rv =
   1299          RemoveAttributeWithTransaction(*resizedElement, *nsGkAtoms::height);
   1300      if (MOZ_UNLIKELY(rv == NS_ERROR_EDITOR_DESTROYED)) {
   1301        NS_WARNING(
   1302            "EditorBase::RemoveAttributeWithTransaction(nsGkAtoms::height) "
   1303            "failed");
   1304        return NS_ERROR_EDITOR_DESTROYED;
   1305      }
   1306      NS_WARNING_ASSERTION(
   1307          NS_SUCCEEDED(rv),
   1308          "EditorBase::RemoveAttributeWithTransaction(nsGkAtoms::height) "
   1309          "failed, but ignored");
   1310    }
   1311 
   1312    if (resizedStyleElement) {
   1313      if (setWidth) {
   1314        nsresult rv = CSSEditUtils::SetCSSPropertyPixelsWithTransaction(
   1315            *this, *resizedStyleElement, *nsGkAtoms::width, width);
   1316        if (NS_FAILED(rv)) {
   1317          NS_WARNING(
   1318              "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::"
   1319              "width) destoyed the editor");
   1320          return NS_ERROR_EDITOR_DESTROYED;
   1321        }
   1322        NS_WARNING_ASSERTION(
   1323            NS_SUCCEEDED(rv),
   1324            "CSSEditUtils::SetCSSPropertyPixels(nsGkAtoms::width) "
   1325            "failed, but ignored");
   1326      }
   1327      if (setHeight) {
   1328        nsresult rv = CSSEditUtils::SetCSSPropertyPixelsWithTransaction(
   1329            *this, *resizedStyleElement, *nsGkAtoms::height, height);
   1330        if (NS_FAILED(rv)) {
   1331          NS_WARNING(
   1332              "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::"
   1333              "height) destoyed the editor");
   1334          return NS_ERROR_EDITOR_DESTROYED;
   1335        }
   1336        NS_WARNING_ASSERTION(
   1337            NS_SUCCEEDED(rv),
   1338            "CSSEditUtils::SetCSSPropertyPixels(nsGkAtoms::height) "
   1339            "failed, but ignored");
   1340      }
   1341    }
   1342  } else {
   1343    // we use HTML size and remove all equivalent CSS properties
   1344 
   1345    // we set the CSS width and height to remove it later,
   1346    // triggering an immediate reflow; otherwise, we have problems
   1347    // with asynchronous reflow
   1348    if (resizedStyleElement) {
   1349      if (setWidth) {
   1350        nsresult rv = CSSEditUtils::SetCSSPropertyPixelsWithTransaction(
   1351            *this, *resizedStyleElement, *nsGkAtoms::width, width);
   1352        if (NS_FAILED(rv)) {
   1353          NS_WARNING(
   1354              "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::"
   1355              "width) destoyed the editor");
   1356          return NS_ERROR_EDITOR_DESTROYED;
   1357        }
   1358        NS_WARNING_ASSERTION(
   1359            NS_SUCCEEDED(rv),
   1360            "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::"
   1361            "width) failed, but ignored");
   1362      }
   1363      if (setHeight) {
   1364        nsresult rv = CSSEditUtils::SetCSSPropertyPixelsWithTransaction(
   1365            *this, *resizedStyleElement, *nsGkAtoms::height, height);
   1366        if (NS_FAILED(rv)) {
   1367          NS_WARNING(
   1368              "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::"
   1369              "height) destoyed the editor");
   1370          return NS_ERROR_EDITOR_DESTROYED;
   1371        }
   1372        NS_WARNING_ASSERTION(
   1373            NS_SUCCEEDED(rv),
   1374            "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::"
   1375            "height) failed, but ignored");
   1376      }
   1377    }
   1378    if (setWidth) {
   1379      nsAutoString w;
   1380      w.AppendInt(width);
   1381      DebugOnly<nsresult> rvIgnored =
   1382          SetAttributeWithTransaction(*resizedElement, *nsGkAtoms::width, w);
   1383      if (NS_WARN_IF(Destroyed())) {
   1384        return NS_ERROR_EDITOR_DESTROYED;
   1385      }
   1386      NS_WARNING_ASSERTION(
   1387          NS_SUCCEEDED(rvIgnored),
   1388          "EditorBase::SetAttributeWithTransaction(nsGkAtoms::width) "
   1389          "failed, but ignored");
   1390    }
   1391    if (setHeight) {
   1392      nsAutoString h;
   1393      h.AppendInt(height);
   1394      DebugOnly<nsresult> rvIgnored =
   1395          SetAttributeWithTransaction(*resizedElement, *nsGkAtoms::height, h);
   1396      if (NS_WARN_IF(Destroyed())) {
   1397        return NS_ERROR_EDITOR_DESTROYED;
   1398      }
   1399      NS_WARNING_ASSERTION(
   1400          NS_SUCCEEDED(rvIgnored),
   1401          "EditorBase::SetAttributeWithTransaction(nsGkAtoms::height) "
   1402          "failed, but ignored");
   1403    }
   1404 
   1405    if (resizedStyleElement) {
   1406      if (setWidth) {
   1407        nsresult rv = CSSEditUtils::RemoveCSSPropertyWithTransaction(
   1408            *this, *resizedStyleElement, *nsGkAtoms::width, u""_ns);
   1409        if (rv == NS_ERROR_EDITOR_DESTROYED) {
   1410          NS_WARNING(
   1411              "CSSEditUtils::RemoveCSSPropertyWithTransaction(nsGkAtoms::width)"
   1412              " destroyed the editor");
   1413          return NS_ERROR_EDITOR_DESTROYED;
   1414        }
   1415        NS_WARNING_ASSERTION(
   1416            NS_SUCCEEDED(rv),
   1417            "CSSEditUtils::RemoveCSSPropertyWithTransaction(nsGkAtoms::width) "
   1418            "failed, but ignored");
   1419      }
   1420      if (setHeight) {
   1421        nsresult rv = CSSEditUtils::RemoveCSSPropertyWithTransaction(
   1422            *this, *resizedStyleElement, *nsGkAtoms::height, u""_ns);
   1423        if (rv == NS_ERROR_EDITOR_DESTROYED) {
   1424          NS_WARNING(
   1425              "CSSEditUtils::RemoveCSSPropertyWithTransaction(nsGkAtoms::"
   1426              "height) destroyed the editor");
   1427          return NS_ERROR_EDITOR_DESTROYED;
   1428        }
   1429        NS_WARNING_ASSERTION(
   1430            NS_SUCCEEDED(rv),
   1431            "CSSEditUtils::RemoveCSSPropertyWithTransaction(nsGkAtoms::height) "
   1432            "failed, but ignored");
   1433      }
   1434    }
   1435  }
   1436 
   1437  // keep track of that size
   1438  mResizedObjectWidth = width;
   1439  mResizedObjectHeight = height;
   1440 
   1441  nsresult rv = RefreshResizersInternal();
   1442  if (rv == NS_ERROR_EDITOR_DESTROYED) {
   1443    return NS_ERROR_EDITOR_DESTROYED;
   1444  }
   1445  NS_WARNING_ASSERTION(
   1446      NS_SUCCEEDED(rv),
   1447      "HTMLEditor::RefreshResizersInternal() failed, but ignored");
   1448  return NS_OK;
   1449 }
   1450 
   1451 NS_IMETHODIMP HTMLEditor::GetObjectResizingEnabled(
   1452    bool* aIsObjectResizingEnabled) {
   1453  *aIsObjectResizingEnabled = IsObjectResizerEnabled();
   1454  return NS_OK;
   1455 }
   1456 
   1457 NS_IMETHODIMP HTMLEditor::SetObjectResizingEnabled(
   1458    bool aObjectResizingEnabled) {
   1459  EnableObjectResizer(aObjectResizingEnabled);
   1460  return NS_OK;
   1461 }
   1462 
   1463 NS_IMETHODIMP HTMLEditor::GetIsObjectResizingActive(bool* aIsActive) {
   1464  MOZ_ASSERT(aIsActive);
   1465  *aIsActive = !!mResizedObject;
   1466  return NS_OK;
   1467 }
   1468 
   1469 #undef kTopLeft
   1470 #undef kTop
   1471 #undef kTopRight
   1472 #undef kLeft
   1473 #undef kRight
   1474 #undef kBottomLeft
   1475 #undef kBottom
   1476 #undef kBottomRight
   1477 
   1478 }  // namespace mozilla