Bug 1485929 - Create HTMLEditor::RefreshInlineTableEditingUIInternal() for internal use r=m_kato
authorMasayuki Nakano <masayuki@d-toybox.com>
Tue, 11 Sep 2018 12:10:54 +0000
changeset 435661 acbe78a8fb89d4dc3996c7f6a2242b27354ef5fe
parent 435660 db2ac775e38ade2e7cbbf4318d5866bafaedc2f6
child 435662 031578506842c55e0a66ee9f6b548a34a925baa9
push id34617
push userbtara@mozilla.com
push dateTue, 11 Sep 2018 22:11:01 +0000
treeherdermozilla-central@ba471b021d0e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersm_kato
bugs1485929
milestone64.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1485929 - Create HTMLEditor::RefreshInlineTableEditingUIInternal() for internal use r=m_kato HTMLEditor::RefreshInlineTableEditingUI() is an XPCOM method. Therefore, we should create a non-virtual method for internal use. Additionally, this patch makes related methods safer for nested calls of ShowInlineTableEditingUI() and HideInlineTableEditingUI(). If ShowInlineTableEditingUI() and RefreshInlineTableEditingUIInternal() detects hiding or replacing current UI, they return error to make the callers stop handling anything for new UI. Differential Revision: https://phabricator.services.mozilla.com/D5428
editor/libeditor/HTMLAnonymousNodeEditor.cpp
editor/libeditor/HTMLEditor.h
editor/libeditor/HTMLInlineTableEditor.cpp
editor/nsIHTMLInlineTableEditor.idl
--- a/editor/libeditor/HTMLAnonymousNodeEditor.cpp
+++ b/editor/libeditor/HTMLAnonymousNodeEditor.cpp
@@ -285,18 +285,18 @@ HTMLEditor::DeleteRefToAnonymousNode(Man
 void
 HTMLEditor::HideAnonymousEditingUIs()
 {
   if (mAbsolutelyPositionedObject) {
     HideGrabberInternal();
     NS_ASSERTION(!mAbsolutelyPositionedObject, "HideGrabber failed");
   }
   if (mInlineEditedCell) {
-    HideInlineTableEditingUI();
-    NS_ASSERTION(!mInlineEditedCell, "HideInlineTableEditingUI failed");
+    HideInlineTableEditingUIInternal();
+    NS_ASSERTION(!mInlineEditedCell, "HideInlineTableEditingUIInternal failed");
   }
   if (mResizedObject) {
     DebugOnly<nsresult> rv = HideResizersInternal();
     NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "HideResizersInternal() failed");
     NS_ASSERTION(!mResizedObject, "HideResizersInternal() failed");
   }
 }
 
@@ -310,18 +310,18 @@ HTMLEditor::HideAnonymousEditingUIsIfUnn
     // XXX If we're moving something, we need to cancel or commit the
     //     operation now.
     HideGrabberInternal();
     NS_ASSERTION(!mAbsolutelyPositionedObject, "HideGrabber failed");
   }
   if (!IsInlineTableEditorEnabled() && mInlineEditedCell) {
     // XXX If we're resizing a table element, we need to cancel or commit the
     //     operation now.
-    HideInlineTableEditingUI();
-    NS_ASSERTION(!mInlineEditedCell, "HideInlineTableEditingUI failed");
+    HideInlineTableEditingUIInternal();
+    NS_ASSERTION(!mInlineEditedCell, "HideInlineTableEditingUIInternal failed");
   }
   if (!IsObjectResizerEnabled() && mResizedObject) {
     // XXX If we're resizing something, we need to cancel or commit the
     //     operation now.
     DebugOnly<nsresult> rv = HideResizersInternal();
     NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "HideResizersInternal() failed");
     NS_ASSERTION(!mResizedObject, "HideResizersInternal() failed");
   }
@@ -433,20 +433,18 @@ HTMLEditor::RefereshEditingUI(Selection&
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     NS_ASSERTION(!mResizedObject, "HideResizersInternal() failed");
   }
 
   if (IsInlineTableEditorEnabled() && mInlineEditedCell &&
       mInlineEditedCell != cellElement) {
-    // XXX HideInlineTableEditingUI() won't return error.  Should be change it
-    //     void later.
-    HideInlineTableEditingUI();
-    NS_ASSERTION(!mInlineEditedCell, "HideInlineTableEditingUI failed");
+    HideInlineTableEditingUIInternal();
+    NS_ASSERTION(!mInlineEditedCell, "HideInlineTableEditingUIInternal failed");
   }
 
   // now, let's display all contextual UI for good
   nsIContent* hostContent = GetActiveEditingHost();
 
   if (IsObjectResizerEnabled() && focusElement &&
       IsModifiableNode(*focusElement) && focusElement != hostContent) {
     if (nsGkAtoms::img == focusTagAtom) {
@@ -478,22 +476,22 @@ HTMLEditor::RefereshEditingUI(Selection&
         return rv;
       }
     }
   }
 
   if (IsInlineTableEditorEnabled() && cellElement &&
       IsModifiableNode(*cellElement) && cellElement != hostContent) {
     if (mInlineEditedCell) {
-      nsresult rv = RefreshInlineTableEditingUI();
+      nsresult rv = RefreshInlineTableEditingUIInternal();
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
     } else {
-      nsresult rv = ShowInlineTableEditingUI(cellElement);
+      nsresult rv = ShowInlineTableEditingUIInternal(*cellElement);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
     }
   }
 
   return NS_OK;
 }
--- a/editor/libeditor/HTMLEditor.h
+++ b/editor/libeditor/HTMLEditor.h
@@ -1922,25 +1922,36 @@ protected: // Shouldn't be used by frien
   void AddPositioningOffset(int32_t& aX, int32_t& aY);
   void SnapToGrid(int32_t& newX, int32_t& newY);
   nsresult GrabberClicked();
   nsresult EndMoving();
   nsresult GetTemporaryStyleForFocusedPositionedElement(Element& aElement,
                                                         nsAString& aReturn);
 
   /**
-   * Shows inline table editing UI around a table cell
-   * @param aCell [IN] a DOM Element being a table cell, td or th
+   * Shows inline table editing UI around a <table> element which contains
+   * aCellElement.  This returns error if creating UI is hidden during this,
+   * or detects another set of UI during this.  In such case, callers
+   * shouldn't keep handling anything for the UI.
+   *
+   * @param aCellElement    Must be an <td> or <th> element.
    */
-  nsresult ShowInlineTableEditingUI(Element* aCell);
+  nsresult ShowInlineTableEditingUIInternal(Element& aCellElement);
 
   /**
-   * Hide all inline table editing UI
+   * Hide all inline table editing UI.
    */
-  void HideInlineTableEditingUI();
+  void HideInlineTableEditingUIInternal();
+
+  /**
+   * RefreshInlineTableEditingUIInternal() moves inline table editing UI to
+   * proper position.  This returns error if the UI is hidden or replaced
+   * during moving.
+   */
+  nsresult RefreshInlineTableEditingUIInternal();
 
   /**
    * IsEmptyTextNode() returns true if aNode is a text node and does not have
    * any visible characters.
    */
   bool IsEmptyTextNode(nsINode& aNode);
 
   bool IsSimpleModifiableNode(nsIContent* aContent,
--- a/editor/libeditor/HTMLInlineTableEditor.cpp
+++ b/editor/libeditor/HTMLInlineTableEditor.cpp
@@ -16,91 +16,160 @@
 #include "nsIPresShell.h"
 #include "nsLiteralString.h"
 #include "nsReadableUtils.h"
 #include "nsString.h"
 #include "nscore.h"
 
 namespace mozilla {
 
-// Uncomment the following line if you want to disable
-// table deletion when the only column/row is removed
-// #define DISABLE_TABLE_DELETION 1
-
 NS_IMETHODIMP
 HTMLEditor::SetInlineTableEditingEnabled(bool aIsEnabled)
 {
   EnableInlineTableEditor(aIsEnabled);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 HTMLEditor::GetInlineTableEditingEnabled(bool* aIsEnabled)
 {
   *aIsEnabled = IsInlineTableEditorEnabled();
   return NS_OK;
 }
 
 nsresult
-HTMLEditor::ShowInlineTableEditingUI(Element* aCell)
+HTMLEditor::ShowInlineTableEditingUIInternal(Element& aCellElement)
 {
-  // do nothing if aCell is not a table cell...
-  if (!aCell || !HTMLEditUtils::IsTableCell(aCell)) {
+  if (NS_WARN_IF(!HTMLEditUtils::IsTableCell(&aCellElement))) {
     return NS_OK;
   }
 
-  if (NS_WARN_IF(!IsDescendantOfEditorRoot(aCell))) {
-    return NS_ERROR_UNEXPECTED;
+  if (NS_WARN_IF(!IsDescendantOfEditorRoot(&aCellElement))) {
+    return NS_ERROR_FAILURE;
   }
 
-  if (mInlineEditedCell) {
-    NS_ERROR("call HideInlineTableEditingUI first");
-    return NS_ERROR_UNEXPECTED;
+  if (NS_WARN_IF(mInlineEditedCell)) {
+    return NS_ERROR_FAILURE;
   }
 
+  mInlineEditedCell = &aCellElement;
+
   // the resizers and the shadow will be anonymous children of the body
   RefPtr<Element> bodyElement = GetRoot();
-  NS_ENSURE_TRUE(bodyElement, NS_ERROR_NULL_POINTER);
+  if (NS_WARN_IF(!bodyElement)) {
+    return NS_ERROR_FAILURE;
+  }
 
-  mAddColumnBeforeButton =
-    CreateAnonymousElement(nsGkAtoms::a, *bodyElement,
-                           NS_LITERAL_STRING("mozTableAddColumnBefore"), false);
-  mRemoveColumnButton =
-    CreateAnonymousElement(nsGkAtoms::a, *bodyElement,
-                           NS_LITERAL_STRING("mozTableRemoveColumn"), false);
-  mAddColumnAfterButton =
-    CreateAnonymousElement(nsGkAtoms::a, *bodyElement,
-                           NS_LITERAL_STRING("mozTableAddColumnAfter"), false);
+  do {
+    // The buttons of inline table editor will be children of the <body>
+    // element.  Creating the anonymous elements may cause calling
+    // HideInlineTableEditingUIInternal() via a mutation event listener.
+    // So, we should store new button to a local variable, then, check:
+    //   - whether creating a button is already set to the member or not
+    //   - whether already created buttons are changed to another set
+    // If creating the buttons are canceled, we hit the latter check.
+    // If buttons for another table are created during this, we hit the latter
+    // check too.
+    // If buttons are just created again for same element, we hit the former
+    // check.
+    ManualNACPtr addColumnBeforeButton =
+      CreateAnonymousElement(nsGkAtoms::a, *bodyElement,
+                             NS_LITERAL_STRING("mozTableAddColumnBefore"), false);
+    if (NS_WARN_IF(!addColumnBeforeButton)) {
+      break; // Hide unnecessary buttons created above.
+    }
+    if (NS_WARN_IF(mAddColumnBeforeButton) ||
+        NS_WARN_IF(mInlineEditedCell != &aCellElement)) {
+      return NS_ERROR_FAILURE; // Don't hide another set of buttons.
+    }
+    mAddColumnBeforeButton = std::move(addColumnBeforeButton);
+
+    ManualNACPtr removeColumnButton =
+      CreateAnonymousElement(nsGkAtoms::a, *bodyElement,
+                             NS_LITERAL_STRING("mozTableRemoveColumn"), false);
+    if (NS_WARN_IF(!removeColumnButton)) {
+      break;
+    }
+    if (NS_WARN_IF(mRemoveColumnButton) ||
+        NS_WARN_IF(mInlineEditedCell != &aCellElement)) {
+      return NS_ERROR_FAILURE;
+    }
+    mRemoveColumnButton = std::move(removeColumnButton);
+
+    ManualNACPtr addColumnAfterButton =
+      CreateAnonymousElement(nsGkAtoms::a, *bodyElement,
+                             NS_LITERAL_STRING("mozTableAddColumnAfter"),
+                             false);
+    if (NS_WARN_IF(!addColumnAfterButton)) {
+      break;
+    }
+    if (NS_WARN_IF(mAddColumnAfterButton) ||
+        NS_WARN_IF(mInlineEditedCell != &aCellElement)) {
+      return NS_ERROR_FAILURE;
+    }
+    mAddColumnAfterButton = std::move(addColumnAfterButton);
 
-  mAddRowBeforeButton =
-    CreateAnonymousElement(nsGkAtoms::a, *bodyElement,
-                           NS_LITERAL_STRING("mozTableAddRowBefore"), false);
-  mRemoveRowButton =
-    CreateAnonymousElement(nsGkAtoms::a, *bodyElement,
-                           NS_LITERAL_STRING("mozTableRemoveRow"), false);
-  mAddRowAfterButton =
-    CreateAnonymousElement(nsGkAtoms::a, *bodyElement,
-                           NS_LITERAL_STRING("mozTableAddRowAfter"), false);
+    ManualNACPtr addRowBeforeButton =
+      CreateAnonymousElement(nsGkAtoms::a, *bodyElement,
+                             NS_LITERAL_STRING("mozTableAddRowBefore"), false);
+    if (NS_WARN_IF(!addRowBeforeButton)) {
+      break;
+    }
+    if (NS_WARN_IF(mAddRowBeforeButton) ||
+        NS_WARN_IF(mInlineEditedCell != &aCellElement)) {
+      return NS_ERROR_FAILURE;
+    }
+    mAddRowBeforeButton = std::move(addRowBeforeButton);
+
+    ManualNACPtr removeRowButton =
+      CreateAnonymousElement(nsGkAtoms::a, *bodyElement,
+                             NS_LITERAL_STRING("mozTableRemoveRow"), false);
+    if (NS_WARN_IF(!removeRowButton)) {
+      break;
+    }
+    if (NS_WARN_IF(mRemoveRowButton) ||
+        NS_WARN_IF(mInlineEditedCell != &aCellElement)) {
+      return NS_ERROR_FAILURE;
+    }
+    mRemoveRowButton = std::move(removeRowButton);
 
-  AddMouseClickListener(mAddColumnBeforeButton);
-  AddMouseClickListener(mRemoveColumnButton);
-  AddMouseClickListener(mAddColumnAfterButton);
-  AddMouseClickListener(mAddRowBeforeButton);
-  AddMouseClickListener(mRemoveRowButton);
-  AddMouseClickListener(mAddRowAfterButton);
+    ManualNACPtr addRowAfterButton =
+      CreateAnonymousElement(nsGkAtoms::a, *bodyElement,
+                             NS_LITERAL_STRING("mozTableAddRowAfter"), false);
+    if (NS_WARN_IF(!addRowAfterButton)) {
+      break;
+    }
+    if (NS_WARN_IF(mAddRowAfterButton) ||
+        NS_WARN_IF(mInlineEditedCell != &aCellElement)) {
+      return NS_ERROR_FAILURE;
+    }
+    mAddRowAfterButton = std::move(addRowAfterButton);
 
-  mInlineEditedCell = aCell;
+    AddMouseClickListener(mAddColumnBeforeButton);
+    AddMouseClickListener(mRemoveColumnButton);
+    AddMouseClickListener(mAddColumnAfterButton);
+    AddMouseClickListener(mAddRowBeforeButton);
+    AddMouseClickListener(mRemoveRowButton);
+    AddMouseClickListener(mAddRowAfterButton);
+
+    mHasShownInlineTableEditor = true;
 
-  mHasShownInlineTableEditor = true;
+    nsresult rv = RefreshInlineTableEditingUIInternal();
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+    return NS_OK;
+  } while (true);
 
-  return RefreshInlineTableEditingUI();
+  HideInlineTableEditingUIInternal();
+  return NS_ERROR_FAILURE;
 }
 
 void
-HTMLEditor::HideInlineTableEditingUI()
+HTMLEditor::HideInlineTableEditingUIInternal()
 {
   mInlineEditedCell = nullptr;
 
   RemoveMouseClickListener(mAddColumnBeforeButton);
   RemoveMouseClickListener(mRemoveColumnButton);
   RemoveMouseClickListener(mAddColumnAfterButton);
   RemoveMouseClickListener(mAddRowBeforeButton);
   RemoveMouseClickListener(mRemoveRowButton);
@@ -110,17 +179,17 @@ HTMLEditor::HideInlineTableEditingUI()
   nsCOMPtr<nsIPresShell> ps = GetPresShell();
   // 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.
 
   // Calling DeleteRefToAnonymousNode() may cause showing the UI again.
   // Therefore, we should forget all anonymous contents first.
   // Otherwise, we could leak the old content because of overwritten by
-  // ShowInlineTableEditingUI().
+  // ShowInlineTableEditingUIInternal().
   ManualNACPtr addColumnBeforeButton(std::move(mAddColumnBeforeButton));
   ManualNACPtr removeColumnButton(std::move(mRemoveColumnButton));
   ManualNACPtr addColumnAfterButton(std::move(mAddColumnAfterButton));
   ManualNACPtr addRowBeforeButton(std::move(mAddRowBeforeButton));
   ManualNACPtr removeRowButton(std::move(mRemoveRowButton));
   ManualNACPtr addRowAfterButton(std::move(mAddRowAfterButton));
 
   DeleteRefToAnonymousNode(std::move(addColumnBeforeButton), ps);
@@ -154,37 +223,33 @@ HTMLEditor::DoInlineTableEditingAction(c
   } else if (anonclass.EqualsLiteral("mozTableAddColumnAfter")) {
     InsertTableColumn(1, true);
   } else if (anonclass.EqualsLiteral("mozTableAddRowBefore")) {
     InsertTableRow(1, false);
   } else if (anonclass.EqualsLiteral("mozTableAddRowAfter")) {
     InsertTableRow(1, true);
   } else if (anonclass.EqualsLiteral("mozTableRemoveColumn")) {
     DeleteTableColumn(1);
-#ifndef DISABLE_TABLE_DELETION
     hideUI = (colCount == 1);
-#endif
   } else if (anonclass.EqualsLiteral("mozTableRemoveRow")) {
     DeleteTableRow(1);
-#ifndef DISABLE_TABLE_DELETION
     hideUI = (rowCount == 1);
-#endif
   } else {
     return NS_OK;
   }
 
   ++mInlineTableEditorUsedCount;
 
   // InsertTableRow might causes reframe
   if (Destroyed()) {
     return NS_OK;
   }
 
   if (hideUI) {
-    HideInlineTableEditingUI();
+    HideInlineTableEditingUIInternal();
     if (hideResizersWithInlineTableUI) {
       DebugOnly<nsresult> rv = HideResizersInternal();
       NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to hide resizers");
     }
   }
 
   return NS_OK;
 }
@@ -205,68 +270,87 @@ HTMLEditor::RemoveMouseClickListener(Ele
     aElement->RemoveEventListener(NS_LITERAL_STRING("click"),
                                   mEventListener, true);
   }
 }
 
 NS_IMETHODIMP
 HTMLEditor::RefreshInlineTableEditingUI()
 {
+  nsresult rv = RefreshInlineTableEditingUIInternal();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+  return NS_OK;
+}
+
+nsresult
+HTMLEditor::RefreshInlineTableEditingUIInternal()
+{
   if (!mInlineEditedCell) {
    return NS_OK;
   }
 
-  RefPtr<nsGenericHTMLElement> htmlElement =
+  RefPtr<nsGenericHTMLElement> inlineEditingCellElement =
     nsGenericHTMLElement::FromNode(mInlineEditedCell);
-  if (!htmlElement) {
-    return NS_ERROR_NULL_POINTER;
+  if (NS_WARN_IF(!inlineEditingCellElement)) {
+    return NS_ERROR_FAILURE;
   }
 
-  int32_t xCell, yCell, wCell, hCell;
-  GetElementOrigin(*mInlineEditedCell, xCell, yCell);
+  int32_t cellX = 0, cellY = 0;
+  GetElementOrigin(*mInlineEditedCell, cellX, cellY);
 
-  wCell = htmlElement->OffsetWidth();
-  hCell = htmlElement->OffsetHeight();
+  int32_t cellWidth = inlineEditingCellElement->OffsetWidth();
+  int32_t cellHeight = inlineEditingCellElement->OffsetHeight();
 
-  int32_t xHoriz = xCell + wCell/2;
-  int32_t yVert  = yCell + hCell/2;
+  int32_t centerOfCellX = cellX + cellWidth / 2;
+  int32_t centerOfCellY = cellY + cellHeight / 2;
 
   RefPtr<Element> tableElement = GetEnclosingTable(mInlineEditedCell);
-  int32_t rowCount, colCount;
+  int32_t rowCount = 0, colCount = 0;
   nsresult rv = GetTableSize(tableElement, &rowCount, &colCount);
-  NS_ENSURE_SUCCESS(rv, rv);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
 
-  SetAnonymousElementPosition(xHoriz-10, yCell-7,  mAddColumnBeforeButton);
-#ifdef DISABLE_TABLE_DELETION
-  if (colCount== 1) {
-    mRemoveColumnButton->SetAttr(kNameSpaceID_None, nsGkAtoms::_class,
-                                 NS_LITERAL_STRING("hidden"), true);
-  } else {
-    if (mRemoveColumnButton->HasAttr(kNameSpaceID_None, nsGkAtoms::_class)) {
-      mRemoveColumnButton->UnsetAttr(kNameSpaceID_None, nsGkAtoms::_class);
-    }
-#endif
-    SetAnonymousElementPosition(xHoriz-4, yCell-7,  mRemoveColumnButton);
-#ifdef DISABLE_TABLE_DELETION
+  RefPtr<Element> addColumunBeforeButton = mAddColumnBeforeButton.get();
+  SetAnonymousElementPosition(centerOfCellX - 10, cellY - 7,
+                              addColumunBeforeButton);
+  if (NS_WARN_IF(addColumunBeforeButton != mAddColumnBeforeButton.get())) {
+    return NS_ERROR_FAILURE;
+  }
+
+  RefPtr<Element> removeColumnButton = mRemoveColumnButton.get();
+  SetAnonymousElementPosition(centerOfCellX - 4, cellY - 7, removeColumnButton);
+  if (NS_WARN_IF(removeColumnButton != mRemoveColumnButton.get())) {
+    return NS_ERROR_FAILURE;
   }
-#endif
-  SetAnonymousElementPosition(xHoriz+6, yCell-7,  mAddColumnAfterButton);
+
+  RefPtr<Element> addColumnAfterButton = mAddColumnAfterButton.get();
+  SetAnonymousElementPosition(centerOfCellX + 6, cellY - 7,
+                              addColumnAfterButton);
+  if (NS_WARN_IF(addColumnAfterButton != mAddColumnAfterButton.get())) {
+    return NS_ERROR_FAILURE;
+  }
 
-  SetAnonymousElementPosition(xCell-7, yVert-10,  mAddRowBeforeButton);
-#ifdef DISABLE_TABLE_DELETION
-  if (rowCount== 1) {
-    mRemoveRowButton->SetAttr(kNameSpaceID_None, nsGkAtoms::_class,
-                              NS_LITERAL_STRING("hidden"), true);
-  } else {
-    if (mRemoveRowButton->HasAttr(kNameSpaceID_None, nsGkAtoms::_class)) {
-      mRemoveRowButton->UnsetAttr(kNameSpaceID_None, nsGkAtoms::_class, true);
-    }
-#endif
-    SetAnonymousElementPosition(xCell-7, yVert-4,  mRemoveRowButton);
-#ifdef DISABLE_TABLE_DELETION
+  RefPtr<Element> addRowBeforeButton = mAddRowBeforeButton.get();
+  SetAnonymousElementPosition(cellX - 7, centerOfCellY - 10,
+                              addRowBeforeButton);
+  if (NS_WARN_IF(addRowBeforeButton != mAddRowBeforeButton.get())) {
+    return NS_ERROR_FAILURE;
   }
-#endif
-  SetAnonymousElementPosition(xCell-7, yVert+6,  mAddRowAfterButton);
+
+  RefPtr<Element> removeRowButton = mRemoveRowButton.get();
+  SetAnonymousElementPosition(cellX - 7, centerOfCellY - 4, removeRowButton);
+  if (NS_WARN_IF(removeRowButton != mRemoveRowButton.get())) {
+    return NS_ERROR_FAILURE;
+  }
+
+  RefPtr<Element> addRowAfterButton = mAddRowAfterButton.get();
+  SetAnonymousElementPosition(cellX - 7, centerOfCellY + 6, addRowAfterButton);
+  if (NS_WARN_IF(addRowAfterButton != mAddRowAfterButton.get())) {
+    return NS_ERROR_FAILURE;
+  }
 
   return NS_OK;
 }
 
 } // namespace mozilla
--- a/editor/nsIHTMLInlineTableEditor.idl
+++ b/editor/nsIHTMLInlineTableEditor.idl
@@ -14,13 +14,17 @@ interface nsIHTMLInlineTableEditor : nsI
    * boolean indicating if inline table editing is enabled in the editor.
    * When inline table editing is enabled, and when the selection is
    * contained in a table cell, special buttons allowing to add/remove
    * a line/column are available on the cell's border.
    */
   attribute boolean inlineTableEditingEnabled;
 
   /**
-   * Refresh already visible inline table editing UI
+   * Refresh already visible inline table editing UI.
+   * If inline table editing UI is not visible, this does nothing.
+   * If the set of inline table editing UI is hidden or replaced with new
+   * one while this is called, this throws an exception.
+   * FYI: Current user in script is only BlueGriffon.
    */
   void refreshInlineTableEditingUI();
 };