Bug 1533293 - part 3: Make editor and ContentEventHandler not use Selection::Extend() due to too slow r=m_kato
☠☠ backed out by bfc174c4deb8 ☠ ☠
authorMasayuki Nakano <masayuki@d-toybox.com>
Mon, 18 Mar 2019 01:52:36 +0000
changeset 525612 d011dfe8368374923cb69a0ab510d6814cbf3ab1
parent 525611 e536f6e123d8f54d5bf165e5e78da13c71a901af
child 525613 a9a91a32262e099d6366cb2c7500db10f551bf13
push id2032
push userffxbld-merge
push dateMon, 13 May 2019 09:36:57 +0000
treeherdermozilla-release@455c1065dcbe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersm_kato
bugs1533293
milestone67.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 1533293 - part 3: Make editor and ContentEventHandler not use Selection::Extend() due to too slow r=m_kato `Selection::Extend()` is too slow but editor and ContentEventHandler use it in some places. We should make them use `Selection::SetStartAndEndInLimiter()` or `Selection::SetBaseAndExtentInLimiter()`. The former is usable only when caller guarantees the start point is prior to the end point in the DOM tree. Otherwise, we need to use the latter even though it's slower than the former. Differential Revision: https://phabricator.services.mozilla.com/D23462
dom/events/ContentEventHandler.cpp
dom/events/ContentEventHandler.h
dom/events/EventStateManager.cpp
dom/events/IMEStateManager.h
dom/events/TextComposition.h
editor/libeditor/EditorBase.h
editor/libeditor/HTMLEditRules.cpp
editor/libeditor/HTMLEditRules.h
editor/libeditor/HTMLEditor.cpp
editor/libeditor/HTMLEditor.h
editor/libeditor/HTMLEditorEventListener.cpp
editor/libeditor/HTMLTableEditor.cpp
editor/libeditor/TextEditRules.cpp
editor/libeditor/TextEditor.h
editor/nsIEditorSpellCheck.idl
editor/nsIHTMLEditor.idl
editor/nsITableEditor.idl
editor/spellchecker/EditorSpellCheck.cpp
editor/spellchecker/TextServicesDocument.cpp
editor/spellchecker/TextServicesDocument.h
extensions/spellcheck/src/mozSpellChecker.cpp
extensions/spellcheck/src/mozSpellChecker.h
--- a/dom/events/ContentEventHandler.cpp
+++ b/dom/events/ContentEventHandler.cpp
@@ -2976,42 +2976,38 @@ nsresult ContentEventHandler::OnSelectio
   int32_t endNodeOffset = rawRange.EndOffset();
   AdjustRangeForSelection(mRootContent, &startNode, &startNodeOffset);
   AdjustRangeForSelection(mRootContent, &endNode, &endNodeOffset);
   if (NS_WARN_IF(!startNode) || NS_WARN_IF(!endNode) ||
       NS_WARN_IF(startNodeOffset < 0) || NS_WARN_IF(endNodeOffset < 0)) {
     return NS_ERROR_UNEXPECTED;
   }
 
-  mSelection->StartBatchChanges();
-
-  // Clear selection first before setting
-  rv = mSelection->RemoveAllRangesTemporarily();
-  // Need to call EndBatchChanges at the end even if call failed
-  if (NS_SUCCEEDED(rv)) {
-    if (aEvent->mReversed) {
-      rv = mSelection->Collapse(endNode, endNodeOffset);
-    } else {
-      rv = mSelection->Collapse(startNode, startNodeOffset);
+  if (aEvent->mReversed) {
+    nsCOMPtr<nsINode> startNodeStrong(startNode);
+    nsCOMPtr<nsINode> endNodeStrong(endNode);
+    ErrorResult error;
+    MOZ_KnownLive(mSelection)
+        ->SetBaseAndExtentInLimiter(*endNodeStrong, endNodeOffset,
+                                    *startNodeStrong, startNodeOffset, error);
+    if (NS_WARN_IF(error.Failed())) {
+      return error.StealNSResult();
     }
-    if (NS_SUCCEEDED(rv) &&
-        (startNode != endNode || startNodeOffset != endNodeOffset)) {
-      if (aEvent->mReversed) {
-        rv = mSelection->Extend(startNode, startNodeOffset);
-      } else {
-        rv = mSelection->Extend(endNode, endNodeOffset);
-      }
+  } else {
+    nsCOMPtr<nsINode> startNodeStrong(startNode);
+    nsCOMPtr<nsINode> endNodeStrong(endNode);
+    ErrorResult error;
+    MOZ_KnownLive(mSelection)
+        ->SetBaseAndExtentInLimiter(*startNodeStrong, startNodeOffset,
+                                    *endNodeStrong, endNodeOffset, error);
+    if (NS_WARN_IF(error.Failed())) {
+      return error.StealNSResult();
     }
   }
 
-  // Pass the eSetSelection events reason along with the BatchChange-end
-  // selection change notifications.
-  mSelection->EndBatchChanges(aEvent->mReason);
-  NS_ENSURE_SUCCESS(rv, rv);
-
   mSelection->ScrollIntoView(nsISelectionController::SELECTION_FOCUS_REGION,
                              nsIPresShell::ScrollAxis(),
                              nsIPresShell::ScrollAxis(), 0);
   aEvent->mSucceeded = true;
   return NS_OK;
 }
 
 nsRect ContentEventHandler::FrameRelativeRect::RectRelativeTo(
--- a/dom/events/ContentEventHandler.h
+++ b/dom/events/ContentEventHandler.h
@@ -118,16 +118,17 @@ class MOZ_STACK_CLASS ContentEventHandle
   // eQuerySelectionAsTransferable event handler
   nsresult OnQuerySelectionAsTransferable(WidgetQueryContentEvent* aEvent);
   // eQueryCharacterAtPoint event handler
   nsresult OnQueryCharacterAtPoint(WidgetQueryContentEvent* aEvent);
   // eQueryDOMWidgetHittest event handler
   nsresult OnQueryDOMWidgetHittest(WidgetQueryContentEvent* aEvent);
 
   // NS_SELECTION_* event
+  MOZ_CAN_RUN_SCRIPT
   nsresult OnSelectionEvent(WidgetSelectionEvent* aEvent);
 
  protected:
   RefPtr<dom::Document> mDocument;
   // mSelection is typically normal selection but if OnQuerySelectedText()
   // is called, i.e., handling eQuerySelectedText, it's the specified selection
   // by WidgetQueryContentEvent::mInput::mSelectionType.
   RefPtr<Selection> mSelection;
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -745,20 +745,22 @@ nsresult EventStateManager::PreHandleEve
 
       // Init lineOrPageDelta values for line scroll events for some devices
       // on some platforms which might dispatch wheel events which don't have
       // lineOrPageDelta values.  And also, if delta values are customized by
       // prefs, this recomputes them.
       DeltaAccumulator::GetInstance()->InitLineOrPageDelta(aTargetFrame, this,
                                                            wheelEvent);
     } break;
-    case eSetSelection:
-      IMEStateManager::HandleSelectionEvent(aPresContext, GetFocusedContent(),
+    case eSetSelection: {
+      nsCOMPtr<nsIContent> focusedContent = GetFocusedContent();
+      IMEStateManager::HandleSelectionEvent(aPresContext, focusedContent,
                                             aEvent->AsSelectionEvent());
       break;
+    }
     case eContentCommandCut:
     case eContentCommandCopy:
     case eContentCommandPaste:
     case eContentCommandDelete:
     case eContentCommandUndo:
     case eContentCommandRedo:
     case eContentCommandPasteTransferable:
     case eContentCommandLookUpDictionary:
--- a/dom/events/IMEStateManager.h
+++ b/dom/events/IMEStateManager.h
@@ -200,16 +200,17 @@ class IMEStateManager {
       WidgetCompositionEvent* aCompositionEvent, nsEventStatus* aStatus,
       EventDispatchingCallback* aCallBack, bool aIsSynthesized = false);
 
   /**
    * All selection events must be handled via HandleSelectionEvent()
    * because they must be handled by same target as composition events when
    * there is a composition.
    */
+  MOZ_CAN_RUN_SCRIPT
   static void HandleSelectionEvent(nsPresContext* aPresContext,
                                    nsIContent* aEventTargetContent,
                                    WidgetSelectionEvent* aSelectionEvent);
 
   /**
    * This is called when PresShell ignores a composition event due to not safe
    * to dispatch events.
    */
--- a/dom/events/TextComposition.h
+++ b/dom/events/TextComposition.h
@@ -451,19 +451,23 @@ class TextComposition final {
                      nsEventStatus* aStatus,
                      EventDispatchingCallback* aCallback,
                      const WidgetCompositionEvent* aOriginalEvent = nullptr);
 
   /**
    * HandleSelectionEvent() sends the selection event to ContentEventHandler
    * or dispatches it to the focused child process.
    */
+  MOZ_CAN_RUN_SCRIPT
   void HandleSelectionEvent(WidgetSelectionEvent* aSelectionEvent) {
-    HandleSelectionEvent(mPresContext, mTabParent, aSelectionEvent);
+    RefPtr<nsPresContext> presContext(mPresContext);
+    RefPtr<TabParent> tabParent(mTabParent);
+    HandleSelectionEvent(presContext, tabParent, aSelectionEvent);
   }
+  MOZ_CAN_RUN_SCRIPT
   static void HandleSelectionEvent(nsPresContext* aPresContext,
                                    TabParent* aTabParent,
                                    WidgetSelectionEvent* aSelectionEvent);
 
   /**
    * MaybeDispatchCompositionUpdate() may dispatch a compositionupdate event
    * if aCompositionEvent changes composition string.
    * @return Returns false if dispatching the compositionupdate event caused
--- a/editor/libeditor/EditorBase.h
+++ b/editor/libeditor/EditorBase.h
@@ -384,16 +384,17 @@ class EditorBase : public nsIEditor,
   }
   bool RemoveTransactionListener(nsITransactionListener& aListener) {
     if (!mTransactionManager) {
       return false;
     }
     return mTransactionManager->RemoveTransactionListener(aListener);
   }
 
+  MOZ_CAN_RUN_SCRIPT
   virtual nsresult HandleKeyPressEvent(WidgetKeyboardEvent* aKeyboardEvent);
 
   virtual dom::EventTarget* GetDOMEventTarget() = 0;
 
   /**
    * Accessor methods to flags.
    */
   uint32_t Flags() const { return mFlags; }
--- a/editor/libeditor/HTMLEditRules.cpp
+++ b/editor/libeditor/HTMLEditRules.cpp
@@ -1726,17 +1726,18 @@ EditActionResult HTMLEditRules::WillInse
 
   if (host == blockParent && separator != ParagraphSeparator::br) {
     // Insert a new block first
     MOZ_ASSERT(separator == ParagraphSeparator::div ||
                separator == ParagraphSeparator::p);
     // MakeBasicBlock() creates AutoSelectionRestorer.
     // Therefore, even if it returns NS_OK, editor might have been destroyed
     // at restoring Selection.
-    nsresult rv = MakeBasicBlock(ParagraphSeparatorElement(separator));
+    OwningNonNull<nsAtom> separatorTag = ParagraphSeparatorElement(separator);
+    nsresult rv = MakeBasicBlock(separatorTag);
     if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED) ||
         NS_WARN_IF(!CanHandleEditAction())) {
       return EditActionIgnored(NS_ERROR_EDITOR_DESTROYED);
     }
     // We warn on failure, but don't handle it, because it might be harmless.
     // Instead we just check that a new block was actually created.
     NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
                          "HTMLEditRules::MakeBasicBlock() failed");
@@ -2216,17 +2217,18 @@ nsresult HTMLEditRules::WillDeleteSelect
   }
 
   // First check for table selection mode.  If so, hand off to table editor.
   ErrorResult error;
   RefPtr<Element> cellElement =
       HTMLEditorRef().GetFirstSelectedTableCellElement(error);
   if (cellElement) {
     error.SuppressException();
-    nsresult rv = HTMLEditorRef().DeleteTableCellContentsWithTransaction();
+    nsresult rv =
+        MOZ_KnownLive(HTMLEditorRef()).DeleteTableCellContentsWithTransaction();
     if (NS_WARN_IF(!CanHandleEditAction())) {
       return NS_ERROR_EDITOR_DESTROYED;
     }
     *aHandled = true;
     return rv;
   }
   nsresult rv = error.StealNSResult();
   cellElement = nullptr;
@@ -6503,23 +6505,16 @@ nsresult HTMLEditRules::ExpandSelectionF
         }
         selEndNode = wsObj.mEndReasonNode->GetParentNode();
         selEndOffset = 1 + selEndNode->ComputeIndexOf(wsObj.mEndReasonNode);
       } else {
         break;
       }
     }
   }
-  // Now set the selection to the new range
-  DebugOnly<nsresult> rv =
-      SelectionRefPtr()->Collapse(selStartNode, selStartOffset);
-  if (NS_WARN_IF(!CanHandleEditAction())) {
-    return NS_ERROR_EDITOR_DESTROYED;
-  }
-  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to collapse selection");
 
   // Expand selection endpoint only if we didn't pass a <br>, or if we really
   // needed to pass that <br> (i.e., its block is now totally selected).
   bool doEndExpansion = true;
   if (firstBRParent) {
     // Find block node containing <br>.
     nsCOMPtr<Element> brBlock = HTMLEditor::GetBlock(*firstBRParent);
     bool nodeBefore = false, nodeAfter = false;
@@ -6537,36 +6532,30 @@ nsresult HTMLEditRules::ExpandSelectionF
       nsRange::CompareNodeToRange(brBlock, range, &nodeBefore, &nodeAfter);
     }
 
     // If block isn't contained, forgo grabbing the <br> in expanded selection.
     if (nodeBefore || nodeAfter) {
       doEndExpansion = false;
     }
   }
-  if (doEndExpansion) {
-    nsresult rv = SelectionRefPtr()->Extend(selEndNode, selEndOffset);
-    if (NS_WARN_IF(!CanHandleEditAction())) {
-      return NS_ERROR_EDITOR_DESTROYED;
-    }
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-  } else {
-    // Only expand to just before <br>.
-    nsresult rv = SelectionRefPtr()->Extend(firstBRParent, firstBROffset);
-    if (NS_WARN_IF(!CanHandleEditAction())) {
-      return NS_ERROR_EDITOR_DESTROYED;
-    }
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-  }
-
-  return NS_OK;
+
+  EditorRawDOMPoint newSelectionStart(selStartNode, selStartOffset);
+  EditorRawDOMPoint newSelectionEnd(
+      doEndExpansion ? selEndNode : firstBRParent,
+      doEndExpansion ? selEndOffset : firstBROffset);
+  ErrorResult error;
+  MOZ_KnownLive(SelectionRefPtr())
+      ->SetStartAndEndInLimiter(newSelectionStart, newSelectionEnd, error);
+  if (NS_WARN_IF(!CanHandleEditAction())) {
+    error.SuppressException();
+    return NS_ERROR_EDITOR_DESTROYED;
+  }
+  NS_WARNING_ASSERTION(!error.Failed(), "Failed to set selection for deletion");
+  return error.StealNSResult();
 }
 
 nsresult HTMLEditRules::NormalizeSelection() {
   MOZ_ASSERT(IsEditorDataAvailable());
 
   // NormalizeSelection() tweaks non-collapsed selections to be more "natural".
   // Idea here is to adjust selection endpoint so that they do not cross breaks
   // or block boundaries unless something editable beyond that boundary is also
@@ -6709,30 +6698,28 @@ nsresult HTMLEditRules::NormalizeSelecti
     return NS_OK;  // New end before old start.
   }
   comp = nsContentUtils::ComparePoints(newStartNode, newStartOffset, endNode,
                                        endOffset);
   if (comp == 1) {
     return NS_OK;  // New start after old end.
   }
 
-  // otherwise set selection to new values.
-  // XXX Why don't we use SetBaseAndExtent()?
-  DebugOnly<nsresult> rv =
-      SelectionRefPtr()->Collapse(newStartNode, newStartOffset);
+  // Otherwise set selection to new values.  Note that end point may be prior
+  // to start point.  So, we cannot use Selection::SetStartAndEndInLimit() here.
+  ErrorResult error;
+  MOZ_KnownLive(SelectionRefPtr())
+      ->SetBaseAndExtentInLimiter(*newStartNode, newStartOffset, *newEndNode,
+                                  newEndOffset, error);
   if (NS_WARN_IF(!CanHandleEditAction())) {
+    error.SuppressException();
     return NS_ERROR_EDITOR_DESTROYED;
   }
-  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to collapse selection");
-  rv = SelectionRefPtr()->Extend(newEndNode, newEndOffset);
-  if (NS_WARN_IF(!CanHandleEditAction())) {
-    return NS_ERROR_EDITOR_DESTROYED;
-  }
-  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to extend selection");
-  return NS_OK;
+  NS_WARNING_ASSERTION(!error.Failed(), "Failed to set selection");
+  return error.StealNSResult();
 }
 
 EditorDOMPoint HTMLEditRules::GetPromotedPoint(RulesEndpoint aWhere,
                                                nsINode& aNode, int32_t aOffset,
                                                EditSubAction aEditSubAction) {
   MOZ_ASSERT(IsEditorDataAvailable());
 
   // we do one thing for text actions, something else entirely for other
--- a/editor/libeditor/HTMLEditRules.h
+++ b/editor/libeditor/HTMLEditRules.h
@@ -225,16 +225,17 @@ class HTMLEditRules : public TextEditRul
    * Called before deleting selected contents.  This method actually removes
    * selected contents.
    *
    * @param aAction             Direction of the deletion.
    * @param aStripWrappers      Must be eStrip or eNoStrip.
    * @param aCancel             Returns true if the operation is canceled.
    * @param aHandled            Returns true if the edit action is handled.
    */
+  MOZ_CAN_RUN_SCRIPT
   MOZ_MUST_USE nsresult WillDeleteSelection(
       nsIEditor::EDirection aAction, nsIEditor::EStripWrappers aStripWrappers,
       bool* aCancel, bool* aHandled);
 
   /**
    * Called after deleting selected content.
    * This method removes unnecessary empty nodes and/or inserts <br> if
    * necessary.
@@ -369,77 +370,84 @@ class HTMLEditRules : public TextEditRul
    *
    */
   MOZ_MUST_USE nsresult
   DeleteElementsExceptTableRelatedElements(nsINode& aNode);
 
   /**
    * XXX Should document what this does.
    */
+  MOZ_CAN_RUN_SCRIPT
   MOZ_MUST_USE nsresult WillMakeList(const nsAString* aListType,
                                      bool aEntireList,
                                      const nsAString* aBulletType,
                                      bool* aCancel, bool* aHandled,
                                      const nsAString* aItemType = nullptr);
 
   /**
    * Called before removing a list element.  This method actually removes
    * list elements and list item elements at Selection.  And move contents
    * in them where the removed list was.
    *
    * @param aCancel             Returns true if the operation is canceled.
    * @param aHandled            Returns true if the edit action is handled.
    */
+  MOZ_CAN_RUN_SCRIPT
   MOZ_MUST_USE nsresult WillRemoveList(bool* aCancel, bool* aHandled);
 
   /**
    * Called before indenting around Selection.  This method actually tries to
    * indent the contents.
    *
    * @param aCancel             Returns true if the operation is canceled.
    * @param aHandled            Returns true if the edit action is handled.
    */
+  MOZ_CAN_RUN_SCRIPT
   MOZ_MUST_USE nsresult WillIndent(bool* aCancel, bool* aHandled);
 
   /**
    * Called before indenting around Selection and it's in CSS mode.
    * This method actually tries to indent the contents.
    *
    * @param aCancel             Returns true if the operation is canceled.
    * @param aHandled            Returns true if the edit action is handled.
    */
+  MOZ_CAN_RUN_SCRIPT
   MOZ_MUST_USE nsresult WillCSSIndent(bool* aCancel, bool* aHandled);
 
   /**
    * Called before indenting around Selection and it's not in CSS mode.
    * This method actually tries to indent the contents.
    *
    * @param aCancel             Returns true if the operation is canceled.
    * @param aHandled            Returns true if the edit action is handled.
    */
+  MOZ_CAN_RUN_SCRIPT
   MOZ_MUST_USE nsresult WillHTMLIndent(bool* aCancel, bool* aHandled);
 
   /**
    * Called before outdenting around Selection.  This method actually tries
    * to indent the contents.
    *
    * @param aCancel             Returns true if the operation is canceled.
    * @param aHandled            Returns true if the edit action is handled.
    */
+  MOZ_CAN_RUN_SCRIPT
   MOZ_MUST_USE nsresult WillOutdent(bool* aCancel, bool* aHandled);
 
   /**
    * Called before aligning contents around Selection.  This method actually
    * sets align attributes to align contents.
    *
    * @param aAlignType          New align attribute value where the contents
    *                            should be aligned to.
    * @param aCancel             Returns true if the operation is canceled.
    * @param aHandled            Returns true if the edit action is handled.
    */
+  MOZ_CAN_RUN_SCRIPT
   nsresult WillAlign(const nsAString& aAlignType, bool* aCancel,
                      bool* aHandled);
 
   /**
    * Called before changing absolute positioned element to static positioned.
    * This method actually changes the position property of nearest absolute
    * positioned element.  Therefore, this might cause destroying the HTML
    * editor.
@@ -468,44 +476,47 @@ class HTMLEditRules : public TextEditRul
    * method just calls WillMakeList() with "dl" as aListType and
    * aDefinitionListItemTag as aItemType.
    *
    * @param aDefinitionListItemTag  Should be "dt" or "dd".
    * @param aEntireList             XXX not sure
    * @param aCancel                 Returns true if the operation is canceled.
    * @param aHandled                Returns true if the edit action is handled.
    */
+  MOZ_CAN_RUN_SCRIPT
   MOZ_MUST_USE nsresult WillMakeDefListItem(const nsAString* aBlockType,
                                             bool aEntireList, bool* aCancel,
                                             bool* aHandled);
 
   /**
    * WillMakeBasicBlock() called before changing block style around Selection.
    * This method actually does something with calling MakeBasicBlock().
    *
    * @param aBlockType          Necessary block style as string.
    * @param aCancel             Returns true if the operation is canceled.
    * @param aHandled            Returns true if the edit action is handled.
    */
+  MOZ_CAN_RUN_SCRIPT
   MOZ_MUST_USE nsresult WillMakeBasicBlock(const nsAString& aBlockType,
                                            bool* aCancel, bool* aHandled);
 
   /**
    * MakeBasicBlock() applies or clears block style around Selection.
    * This method creates AutoSelectionRestorer.  Therefore, each caller
    * need to check if the editor is still available even if this returns
    * NS_OK.
    *
    * @param aBlockType          New block tag name.
    *                            If nsGkAtoms::normal or nsGkAtoms::_empty,
    *                            RemoveBlockStyle() will be called.
    *                            If nsGkAtoms::blockquote, MakeBlockquote()
    *                            will be called.
    *                            Otherwise, ApplyBlockStyle() will be called.
    */
+  MOZ_CAN_RUN_SCRIPT
   MOZ_MUST_USE nsresult MakeBasicBlock(nsAtom& aBlockType);
 
   /**
    * Called after creating a basic block, indenting, outdenting or aligning
    * contents.  This method inserts moz-<br> element if start container of
    * Selection needs it.
    */
   MOZ_MUST_USE nsresult DidMakeBasicBlock();
@@ -514,16 +525,17 @@ class HTMLEditRules : public TextEditRul
    * Called before changing an element to absolute positioned.
    * This method only prepares the operation since DidAbsolutePosition() will
    * change it actually later.  mNewBlock is set to the target element and
    * if necessary, some ancestor nodes of selection may be split.
    *
    * @param aCancel             Returns true if the operation is canceled.
    * @param aHandled            Returns true if the edit action is handled.
    */
+  MOZ_CAN_RUN_SCRIPT
   MOZ_MUST_USE nsresult WillAbsolutePosition(bool* aCancel, bool* aHandled);
 
   /**
    * PrepareToMakeElementAbsolutePosition() is helper method of
    * WillAbsolutePosition() since in some cases, needs to restore selection
    * with AutoSelectionRestorer.  So, all callers have to check if
    * CanHandleEditAction() still returns true after a call of this method.
    * XXX Should be documented outline of this method.
@@ -843,24 +855,26 @@ class HTMLEditRules : public TextEditRul
                                int32_t aOffset = 0);
 
   /**
    * ExpandSelectionForDeletion() may expand Selection range if it's not
    * collapsed and there is only one range.  This may expand to include
    * invisible <br> element for preventing delete action handler to keep
    * unexpected nodes.
    */
+  MOZ_CAN_RUN_SCRIPT
   MOZ_MUST_USE nsresult ExpandSelectionForDeletion();
 
   /**
    * NormalizeSelection() adjust Selection if it's not collapsed and there is
    * only one range.  If range start and/or end point is <br> node or something
    * non-editable point, they should be moved to nearest text node or something
    * where the other methods easier to handle edit action.
    */
+  MOZ_CAN_RUN_SCRIPT
   MOZ_MUST_USE nsresult NormalizeSelection();
 
   /**
    * GetPromotedPoint() figures out where a start or end point for a block
    * operation really is.
    */
   EditorDOMPoint GetPromotedPoint(RulesEndpoint aWhere, nsINode& aNode,
                                   int32_t aOffset,
--- a/editor/libeditor/HTMLEditor.cpp
+++ b/editor/libeditor/HTMLEditor.cpp
@@ -1722,53 +1722,43 @@ HTMLEditor::SelectElement(Element* aElem
     return NS_ERROR_INVALID_ARG;
   }
 
   AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing);
   if (NS_WARN_IF(!editActionData.CanHandle())) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
-  nsresult rv = SelectContentInternal(*aElement);
+  nsresult rv = SelectContentInternal(MOZ_KnownLive(*aElement));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   return NS_OK;
 }
 
 nsresult HTMLEditor::SelectContentInternal(nsIContent& aContentToSelect) {
   MOZ_ASSERT(IsEditActionDataAvailable());
 
   // Must be sure that element is contained in the document body
   if (!IsDescendantOfEditorRoot(&aContentToSelect)) {
     return NS_ERROR_FAILURE;
   }
 
-  nsINode* parent = aContentToSelect.GetParentNode();
-  if (NS_WARN_IF(!parent)) {
+  EditorRawDOMPoint newSelectionStart(&aContentToSelect);
+  if (NS_WARN_IF(!newSelectionStart.IsSet())) {
     return NS_ERROR_FAILURE;
   }
-
-  // Don't notify selection change at collapse.
-  AutoUpdateViewBatch notifySelectionChangeOnce(*this);
-
-  // XXX Perhaps, Selection should have SelectNode(nsIContent&).
-  int32_t offsetInParent = parent->ComputeIndexOf(&aContentToSelect);
-
-  // Collapse selection to just before desired element,
-  nsresult rv = SelectionRefPtr()->Collapse(parent, offsetInParent);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-  // then extend it to just after
-  rv = SelectionRefPtr()->Extend(parent, offsetInParent + 1);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-  return NS_OK;
+  EditorRawDOMPoint newSelectionEnd(&aContentToSelect);
+  MOZ_ASSERT(newSelectionEnd.IsSet());
+  DebugOnly<bool> advanced = newSelectionEnd.AdvanceOffset();
+  ErrorResult error;
+  MOZ_KnownLive(SelectionRefPtr())
+      ->SetStartAndEndInLimiter(newSelectionStart, newSelectionEnd, error);
+  NS_WARNING_ASSERTION(!error.Failed(), "Failed to select the given content");
+  return error.StealNSResult();
 }
 
 NS_IMETHODIMP
 HTMLEditor::SetCaretAfterElement(Element* aElement) {
   if (NS_WARN_IF(!aElement)) {
     return NS_ERROR_INVALID_ARG;
   }
 
--- a/editor/libeditor/HTMLEditor.h
+++ b/editor/libeditor/HTMLEditor.h
@@ -136,16 +136,17 @@ class HTMLEditor final : public TextEdit
 
   MOZ_CAN_RUN_SCRIPT
   NS_IMETHOD PasteTransferable(nsITransferable* aTransferable) override;
 
   NS_IMETHOD DeleteNode(nsINode* aNode) override;
 
   NS_IMETHOD InsertLineBreak() override;
 
+  MOZ_CAN_RUN_SCRIPT
   virtual nsresult HandleKeyPressEvent(
       WidgetKeyboardEvent* aKeyboardEvent) override;
   virtual nsIContent* GetFocusedContent() override;
   virtual already_AddRefed<nsIContent> GetFocusedContentForIME() override;
   virtual bool IsActiveInDOMWindow() override;
   virtual dom::EventTarget* GetDOMEventTarget() override;
   virtual Element* FindSelectionRoot(nsINode* aNode) const override;
   virtual bool IsAcceptableInputEvent(WidgetGUIEvent* aGUIEvent) override;
@@ -418,16 +419,17 @@ class HTMLEditor final : public TextEdit
     mDefaultParagraphSeparator = aSep;
   }
 
   /**
    * Modifies the table containing the selection according to the
    * activation of an inline table editing UI element
    * @param aUIAnonymousElement [IN] the inline table editing UI element
    */
+  MOZ_CAN_RUN_SCRIPT
   nsresult DoInlineTableEditingAction(const Element& aUIAnonymousElement);
 
   /**
    * GetElementOrParentByTagName() looks for an element node whose name matches
    * aTagName from aNode or anchor node of Selection to <body> element.
    *
    * @param aTagName        The tag name which you want to look for.
    *                        Must not be nsGkAtoms::_empty.
@@ -636,16 +638,17 @@ class HTMLEditor final : public TextEdit
 
   /**
    * DeleteTableCellContentsWithTransaction() removes any contents in cell
    * elements.  If two or more cell elements are selected, this removes
    * all selected cells' contents.  Otherwise, this removes contents of
    * a cell which contains first selection range.  This does not return
    * error even if selection is not in cell element, just does nothing.
    */
+  MOZ_CAN_RUN_SCRIPT
   nsresult DeleteTableCellContentsWithTransaction();
 
   void IsNextCharInNodeWhitespace(nsIContent* aContent, int32_t aOffset,
                                   bool* outIsSpace, bool* outIsNBSP,
                                   nsIContent** outNode = nullptr,
                                   int32_t* outOffset = 0);
   void IsPrevCharInNodeWhitespace(nsIContent* aContent, int32_t aOffset,
                                   bool* outIsSpace, bool* outIsNBSP,
@@ -959,16 +962,17 @@ class HTMLEditor final : public TextEdit
   virtual nsresult SelectAllInternal() override;
 
   /**
    * SelectContentInternal() sets Selection to aContentToSelect to
    * aContentToSelect + 1 in parent of aContentToSelect.
    *
    * @param aContentToSelect    The content which should be selected.
    */
+  MOZ_CAN_RUN_SCRIPT
   nsresult SelectContentInternal(nsIContent& aContentToSelect);
 
   /**
    * CollapseSelectionAfter() collapses Selection after aElement.
    * If aElement is an orphan node or not in editing host, returns error.
    */
   nsresult CollapseSelectionAfter(Element& aElement);
 
@@ -1660,16 +1664,17 @@ class HTMLEditor final : public TextEdit
 
   virtual already_AddRefed<Element> GetInputEventTargetElement() override;
 
   /**
    * Return TRUE if aElement is a table-related elemet and caret was set.
    */
   bool SetCaretInTableCell(dom::Element* aElement);
 
+  MOZ_CAN_RUN_SCRIPT
   nsresult TabInTable(bool inIsShift, bool* outHandled);
 
   /**
    * InsertPosition is an enum to indicate where the method should insert to.
    */
   enum class InsertPosition {
     // Before selected cell or a cell containing first selection range.
     eBeforeSelectedCell,
@@ -1691,46 +1696,49 @@ class HTMLEditor final : public TextEdit
    * for keeping table rectangle.
    * If first selection range is not in table cell element, this does nothing
    * but does not return error.
    *
    * @param aNumberOfCellssToInsert     Number of cells to insert.
    * @param aInsertPosition             Before or after the target cell which
    *                                    contains first selection range.
    */
+  MOZ_CAN_RUN_SCRIPT
   nsresult InsertTableCellsWithTransaction(int32_t aNumberOfCellsToInsert,
                                            InsertPosition aInsertPosition);
 
   /**
    * InsertTableColumnsWithTransaction() inserts columns before or after
    * a cell element containing first selection range.  I.e., if the cell
    * spans columns and aInsertPosition is eAfterSelectedCell, new columns
    * will be inserted after the right-most row which contains the cell.
    * If first selection range is not in table cell element, this does nothing
    * but does not return error.
    *
    * @param aNumberOfColumnsToInsert    Number of columns to insert.
    * @param aInsertPosition             Before or after the target cell which
    *                                    contains first selection range.
    */
+  MOZ_CAN_RUN_SCRIPT
   nsresult InsertTableColumnsWithTransaction(int32_t aNumberOfColumnsToInsert,
                                              InsertPosition aInsertPosition);
 
   /**
    * InsertTableRowsWithTransaction() inserts <tr> elements before or after
    * a cell element containing first selection range.  I.e., if the cell
    * spans rows and aInsertPosition is eAfterSelectedCell, new rows will be
    * inserted after the most-bottom row which contains the cell.  If first
    * selection range is not in table cell element, this does nothing but
    * does not return error.
    *
    * @param aNumberOfRowsToInsert       Number of rows to insert.
    * @param aInsertPosition             Before or after the target cell which
    *                                    contains first selection range.
    */
+  MOZ_CAN_RUN_SCRIPT
   nsresult InsertTableRowsWithTransaction(int32_t aNumberOfRowsToInsert,
                                           InsertPosition aInsertPosition);
 
   /**
    * Insert a new cell after or before supplied aCell.
    * Optional: If aNewCell supplied, returns the newly-created cell (addref'd,
    * of course)
    * This doesn't change or use the current selection.
@@ -1750,31 +1758,33 @@ class HTMLEditor final : public TextEdit
    * If selection is not in a cell element, this does not return error,
    * just does nothing.
    * WARNING: This does not remove <col> nor <colgroup> elements.
    *
    * @param aNumberOfColumnsToDelete    Number of columns to remove.  This is
    *                                    ignored if 2 ore more cells are
    *                                    selected.
    */
+  MOZ_CAN_RUN_SCRIPT
   nsresult DeleteSelectedTableColumnsWithTransaction(
       int32_t aNumberOfColumnsToDelete);
 
   /**
    * DeleteTableColumnWithTransaction() removes cell elements which belong
    * to the specified column.
    * This method adjusts colspan attribute value if cells spanning the
    * column to delete.
    * WARNING: This does not remove <col> nor <colgroup> elements.
    *
    * @param aTableElement       The <table> element which contains the
    *                            column which you want to remove.
    * @param aRowIndex           Index of the column which you want to remove.
    *                            0 is the first column.
    */
+  MOZ_CAN_RUN_SCRIPT
   nsresult DeleteTableColumnWithTransaction(Element& aTableElement,
                                             int32_t aColumnIndex);
 
   /**
    * DeleteSelectedTableRowsWithTransaction() removes <tr> elements.
    * If only one cell element is selected or first selection range is
    * in a cell, removes <tr> elements starting from a <tr> element
    * containing the selected cell or first selection range.
@@ -1783,30 +1793,32 @@ class HTMLEditor final : public TextEdit
    * is ignored.
    * If there is no selection ranges, returns error.
    * If selection is not in a cell element, this does not return error,
    * just does nothing.
    *
    * @param aNumberOfRowsToDelete   Number of rows to remove.  This is ignored
    *                                if 2 or more cells are selected.
    */
+  MOZ_CAN_RUN_SCRIPT
   nsresult DeleteSelectedTableRowsWithTransaction(
       int32_t aNumberOfRowsToDelete);
 
   /**
    * DeleteTableRowWithTransaction() removes a <tr> element whose index in
    * the <table> is aRowIndex.
    * This method adjusts rowspan attribute value if the <tr> element contains
    * cells which spans rows.
    *
    * @param aTableElement       The <table> element which contains the
    *                            <tr> element which you want to remove.
    * @param aRowIndex           Index of the <tr> element which you want to
    *                            remove.  0 is the first row.
    */
+  MOZ_CAN_RUN_SCRIPT
   nsresult DeleteTableRowWithTransaction(Element& aTableElement,
                                          int32_t aRowIndex);
 
   /**
    * DeleteTableCellWithTransaction() removes table cell elements.  If two or
    * more cell elements are selected, this removes all selected cell elements.
    * Otherwise, this removes some cell elements starting from selected cell
    * element or a cell containing first selection range.  When this removes
@@ -1814,16 +1826,17 @@ class HTMLEditor final : public TextEdit
    * <table> too.  Note that when removing a cell causes number of its row
    * becomes less than the others, this method does NOT fill the place with
    * rowspan nor colspan.  This does not return error even if selection is not
    * in cell element, just does nothing.
    *
    * @param aNumberOfCellsToDelete  Number of cells to remove.  This is ignored
    *                                if 2 or more cells are selected.
    */
+  MOZ_CAN_RUN_SCRIPT
   nsresult DeleteTableCellWithTransaction(int32_t aNumberOfCellsToDelete);
 
   /**
    * DeleteAllChildrenWithTransaction() removes all children of aElement from
    * the tree.
    *
    * @param aElement        The element whose children you want to remove.
    */
@@ -2117,16 +2130,17 @@ class HTMLEditor final : public TextEdit
    *                    row (ePreviousRow) or don't search for another cell
    *                    (aNoSearch).  If no cell is found, caret is place just
    *                    before table; and if that fails, at beginning of
    *                    document.  Thus we generally don't worry about the
    *                    return value and can use the
    *                    AutoSelectionSetterAfterTableEdit stack-based object to
    *                    insure we reset the caret in a table-editing method.
    */
+  MOZ_CAN_RUN_SCRIPT
   void SetSelectionAfterTableEdit(Element* aTable, int32_t aRow, int32_t aCol,
                                   int32_t aDirection, bool aSelected);
 
   void RemoveListenerAndDeleteRef(const nsAString& aEvent,
                                   nsIDOMEventListener* aListener,
                                   bool aUseCapture, ManualNACPtr aElement,
                                   PresShell* aPresShell);
   void DeleteRefToAnonymousNode(ManualNACPtr aContent, PresShell* aPresShell);
--- a/editor/libeditor/HTMLEditorEventListener.cpp
+++ b/editor/libeditor/HTMLEditorEventListener.cpp
@@ -253,17 +253,17 @@ nsresult HTMLEditorEventListener::MouseD
   // point.  Then, we won't be able to commit the composition.
   if (!EnsureCommitComposition()) {
     return NS_OK;
   }
 
   WidgetMouseEvent* mousedownEvent =
       aMouseEvent->WidgetEventPtr()->AsMouseEvent();
 
-  HTMLEditor* htmlEditor = mEditorBase->AsHTMLEditor();
+  RefPtr<HTMLEditor> htmlEditor = mEditorBase->AsHTMLEditor();
   MOZ_ASSERT(htmlEditor);
 
   // Contenteditable should disregard mousedowns outside it.
   // IsAcceptableInputEvent() checks it for a mouse event.
   if (!htmlEditor->IsAcceptableInputEvent(mousedownEvent)) {
     return EditorEventListener::MouseDown(aMouseEvent);
   }
 
--- a/editor/libeditor/HTMLTableEditor.cpp
+++ b/editor/libeditor/HTMLTableEditor.cpp
@@ -58,20 +58,22 @@ class MOZ_STACK_CLASS AutoSelectionSette
                                     int32_t aDirection, bool aSelected)
       : mHTMLEditor(&aHTMLEditor),
         mTable(aTable),
         mCol(aCol),
         mRow(aRow),
         mDirection(aDirection),
         mSelected(aSelected) {}
 
+  MOZ_CAN_RUN_SCRIPT
   ~AutoSelectionSetterAfterTableEdit() {
     if (mHTMLEditor) {
-      mHTMLEditor->SetSelectionAfterTableEdit(mTable, mRow, mCol, mDirection,
-                                              mSelected);
+      MOZ_KnownLive(mHTMLEditor)
+          ->SetSelectionAfterTableEdit(MOZ_KnownLive(mTable), mRow, mCol,
+                                       mDirection, mSelected);
     }
   }
 
   // This is needed to abort the caret reset in the destructor
   // when one method yields control to another
   void CancelSetCaret() {
     mHTMLEditor = nullptr;
     mTable = nullptr;
--- a/editor/libeditor/TextEditRules.cpp
+++ b/editor/libeditor/TextEditRules.cpp
@@ -1700,30 +1700,24 @@ nsresult TextEditRules::HideLastPassword
     return NS_OK;
   }
 
   selNode->GetAsText()->ReplaceData(mLastStart, mLastLength, hiddenText,
                                     IgnoreErrors());
   if (NS_WARN_IF(!CanHandleEditAction())) {
     return NS_ERROR_EDITOR_DESTROYED;
   }
-  // XXXbz Selection::Collapse/Extend take int32_t, but there are tons of
-  // callsites... Converting all that is a battle for another day.
-  DebugOnly<nsresult> rv = SelectionRefPtr()->Collapse(selNode, start);
+  IgnoredErrorResult ignoredError;
+  MOZ_KnownLive(SelectionRefPtr())
+      ->SetStartAndEndInLimiter(RawRangeBoundary(selNode, start),
+                                RawRangeBoundary(selNode, end), ignoredError);
   if (NS_WARN_IF(!CanHandleEditAction())) {
     return NS_ERROR_EDITOR_DESTROYED;
   }
-  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to collapse selection");
-  if (start != end) {
-    rv = SelectionRefPtr()->Extend(selNode, end);
-    if (NS_WARN_IF(!CanHandleEditAction())) {
-      return NS_ERROR_EDITOR_DESTROYED;
-    }
-    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to extend selection");
-  }
+  NS_WARNING_ASSERTION(!ignoredError.Failed(), "Failed to set selection");
   return NS_OK;
 }
 
 // static
 void TextEditRules::FillBufWithPWChars(nsAString* aOutString, int32_t aLength) {
   MOZ_ASSERT(aOutString);
 
   // change the output to the platform password character
--- a/editor/libeditor/TextEditor.h
+++ b/editor/libeditor/TextEditor.h
@@ -100,16 +100,17 @@ class TextEditor : public EditorBase, pu
   bool IsEmpty() const {
     bool isEmpty = false;
     nsresult rv = IsEmpty(&isEmpty);
     NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
                          "Checking whether the editor is empty failed");
     return NS_SUCCEEDED(rv) && isEmpty;
   }
 
+  MOZ_CAN_RUN_SCRIPT
   virtual nsresult HandleKeyPressEvent(
       WidgetKeyboardEvent* aKeyboardEvent) override;
 
   virtual dom::EventTarget* GetDOMEventTarget() override;
 
   /**
    * PasteAsAction() pastes clipboard content to Selection.  This method
    * may dispatch ePaste event first.  If its defaultPrevent() is called,
--- a/editor/nsIEditorSpellCheck.idl
+++ b/editor/nsIEditorSpellCheck.idl
@@ -31,16 +31,17 @@ interface nsIEditorSpellCheck : nsISuppo
 
   /**
    * When interactively spell checking the document, this will return the
    * value of the next word that is misspelled. This also computes the
    * suggestions which you can get by calling GetSuggestedWord.
    *
    * @see mozSpellChecker::GetNextMisspelledWord
    */
+  [can_run_script]
   AString       GetNextMisspelledWord();
 
   /**
    * Used to get suggestions for the last word that was checked and found to
    * be misspelled. The first call will give you the first (best) suggestion.
    * Subsequent calls will iterate through all the suggestions, allowing you
    * to build a list. When there are no more suggestions, an empty string
    * (not a null pointer) will be returned.
@@ -59,16 +60,17 @@ interface nsIEditorSpellCheck : nsISuppo
    */
   boolean       CheckCurrentWord(in AString suggestedWord);
 
   /**
    * Use when modally checking the document to replace a word.
    *
    * @see mozSpellChecker::CheckCurrentWord
    */
+  [can_run_script]
   void          ReplaceWord(in AString misspelledWord, in AString replaceWord, in boolean allOccurrences);
 
   /**
    * @see mozSpellChecker::IgnoreAll
    */
   void          IgnoreWordAllOccurrences(in AString word);
 
   /**
--- a/editor/nsIHTMLEditor.idl
+++ b/editor/nsIHTMLEditor.idl
@@ -178,16 +178,17 @@ interface nsIHTMLEditor : nsISupports
   /* ------------ Selection manipulation -------------- */
   /* Should these be moved to Selection? */
 
   /**
     * Set the selection at the suppled element
     *
     * @param aElement   An element in the document
     */
+  [can_run_script]
   void selectElement(in Element aElement);
 
   /**
     * Create a collapsed selection just after aElement
     *
     * XXX could we parameterize SelectElement(before/select/after>?
     *
     * The selection is set to parent-of-aElement with an
--- a/editor/nsITableEditor.idl
+++ b/editor/nsITableEditor.idl
@@ -32,84 +32,90 @@ interface nsITableEditor : nsISupports
    * If first selection range is not in table cell element, this does nothing
    * without exception.
    *
    * @param aNumberOfCellssToInsert     Number of cells to insert.
    * @param aInsertAfterSelectedCell    true if new cells should be inserted
    *                                    before current cell.  Otherwise, will
    *                                    be inserted after the cell.
    */
+  [can_run_script]
   void insertTableCell(in long aNumberOfColumnsToInsert,
                        in boolean aInsertAfterSelectedCell);
 
   /**
    * insertTableColumn() inserts columns before or after a cell element
    * containing first selection range.  I.e., if the cell spans columns and
    * aInsertAfterSelectedCell is tre, new columns will be inserted after the
    * right-most column which contains the cell.  If first selection range is
    * not in table cell element, this does nothing without exception.
    *
    * @param aNumberOfColumnsToInsert    Number of columns to insert.
    * @param aInsertAfterSelectedCell    true if new columns will be inserted
    *                                    before current cell.  Otherwise, will
    *                                    be inserted after the cell.
    */
+  [can_run_script]
   void insertTableColumn(in long aNumberOfColumnsToInsert,
                          in boolean aInsertAfterSelectedCell);
 
   /*
    * insertTableRow() inserts <tr> elements before or after a <td> element
    * containing first selection range.  I.e., if the cell spans rows and
    * aInsertAfterSelectedCell is true, new rows will be inserted after the
    * bottom-most row which contains the cell.  If first selection range is
    * not in table cell element, this does nothing without exception.
    *
    * @param aNumberOfRowsToInsert       Number of rows to insert.
    * @param aInsertAfterSelectedCell    true if new rows will be inserted
    *                                    before current cell.  Otherwise, will
    *                                    be inserted after the cell.
    */
+  [can_run_script]
   void insertTableRow(in long aNumberOfRowsToInsert,
                       in boolean aInsertAfterSelectedCell);
 
   /** Delete table methods
     * Delete starting at the selected cell or the
     *  cell (or table) enclosing the selection anchor
     * The selection is collapsed and is left in the
     *  cell at the same row,col location as
     *  the previous selection anchor, if possible,
     *  else in the closest neigboring cell
     *
     * @param aNumber    Number of items to insert/delete
     */
+  [can_run_script]
   void deleteTable();
 
   /**
    * deleteTableCellContents() removes any contents in cell elements.  If two
    * or more cell elements are selected, this removes all selected cells'
    * contents.  Otherwise, this removes contents of a cell which contains
    * first selection range.  This does nothing without exception if selection
    * is not in cell element.
    */
+  [can_run_script]
   void deleteTableCellContents();
 
   /**
    * deleteTableCell() removes table cell elements.  If two or more cell
    * elements are selected, this removes all selected cell elements.
    * Otherwise, this removes some cell elements starting from selected cell
    * element or a cell containing first selection range.  When this removes
    * last cell element in <tr> or <table>, this removes the <tr> or the
    * <table> too.  Note that when removing a cell causes number of its row
    * becomes less than the others, this method does NOT fill the place with
    * rowspan nor colspan.  This does nothing without exception if selection is
    * not in cell element.
    *
    * @param aNumberOfCellsToDelete  Number of cells to remove.  This is ignored
    *                                if 2 or more cells are selected.
    */
+  [can_run_script]
   void deleteTableCell(in long aNumberOfCellsToDelete);
 
   /**
    * deleteTableColumn() removes cell elements which belong to same columns
    * of selected cell elements.
    * If only one cell element is selected or first selection range is
    * in a cell, removes cell elements which belong to same column.
    * If 2 or more cell elements are selected, removes cell elements which
@@ -119,16 +125,17 @@ interface nsITableEditor : nsISupports
    * If selection is not in a cell element, just does nothing without
    * throwing exception.
    * WARNING: This does not remove <col> nor <colgroup> elements.
    *
    * @param aNumberOfColumnsToDelete    Number of columns to remove.  This is
    *                                    ignored if 2 ore more cells are
    *                                    selected.
    */
+  [can_run_script]
   void deleteTableColumn(in long aNumberOfColumnsToDelete);
 
   /**
    * deleteTableRow() removes <tr> elements.
    * If only one cell element is selected or first selection range is
    * in a cell, removes <tr> elements starting from a <tr> element
    * containing the selected cell or first selection range.
    * If 2 or more cell elements are selected, all <tr> elements
@@ -136,48 +143,56 @@ interface nsITableEditor : nsISupports
    * is ignored.
    * If there is no selection ranges, throws exception.
    * If selection is not in a cell element, just does nothing without
    * throwing exception.
    *
    * @param aNumberOfRowsToDelete   Number of rows to remove.  This is ignored
    *                                if 2 or more cells are selected.
    */
+  [can_run_script]
   void deleteTableRow(in long aNumberOfRowsToDelete);
 
   /** Table Selection methods
     * Selecting a row or column actually
     * selects all cells (not TR in the case of rows)
     */
+  [can_run_script]
   void selectTableCell();
 
   /** Select a rectangular block of cells:
     *  all cells falling within the row/column index of aStartCell
     *  to through the row/column index of the aEndCell
     *  aStartCell can be any location relative to aEndCell,
     *   as long as they are in the same table
     *  @param aStartCell  starting cell in block
     *  @param aEndCell    ending cell in block
     */
+  [can_run_script]
   void selectBlockOfCells(in Element aStartCell,
                           in Element aEndCell);
 
+  [can_run_script]
   void selectTableRow();
+  [can_run_script]
   void selectTableColumn();
+  [can_run_script]
   void selectTable();
+  [can_run_script]
   void selectAllTableCells();
 
   /** Create a new TD or TH element, the opposite type of the supplied aSourceCell
     *   1. Copy all attributes from aSourceCell to the new cell
     *   2. Move all contents of aSourceCell to the new cell
     *   3. Replace aSourceCell in the table with the new cell
     *
     *  @param aSourceCell   The cell to be replaced
     *  @return              The new cell that replaces aSourceCell
     */
+  [can_run_script]
   Element switchTableCellHeaderType(in Element aSourceCell);
 
   /** Merges contents of all selected cells
     * for selected cells that are adjacent,
     * this will result in a larger cell with appropriate
     * rowspan and colspan, and original cells are deleted
     * The resulting cell is in the location of the
     *   cell at the upper-left corner of the adjacent
@@ -190,36 +205,39 @@ interface nsITableEditor : nsISupports
     *         to the upper-left cell
     *       If false: contiguous cells are ignored
     *
     * If there are no selected cells,
     *   and selection or caret is in a cell,
     *   that cell and the one to the right
     *   are merged
     */
+  [can_run_script]
   void joinTableCells(in boolean aMergeNonContiguousContents);
 
   /** Split a cell that has rowspan and/or colspan > 0
     *   into cells such that all new cells have
     *   rowspan = 1 and colspan = 1
     *  All of the contents are not touched --
     *   they will appear to be in the upper-left cell
     */
+  [can_run_script]
   void splitTableCell();
 
   /** Scan through all rows and add cells as needed so
     *   all locations in the cellmap are occupied.
     *   Used after inserting single cells or pasting
     *   a collection of cells that extend past the
     *   previous size of the table
     * If aTable is null, it uses table enclosing the selection anchor
     * This doesn't doesn't change the selection,
     *   thus it can be used to fixup all tables
     *   in a page independent of the selection
     */
+  [can_run_script]
   void normalizeTable(in Element aTable);
 
   /**
    * getCellIndexes() computes row index and column index of a table cell.
    * Note that this depends on layout information.  Therefore, all pending
    * layout should've been flushed before calling this.
    *
    * @param aCellElement        If not null, this computes indexes of the cell.
--- a/editor/spellchecker/EditorSpellCheck.cpp
+++ b/editor/spellchecker/EditorSpellCheck.cpp
@@ -402,18 +402,19 @@ EditorSpellCheck::InitSpellChecker(nsIEd
 
 NS_IMETHODIMP
 EditorSpellCheck::GetNextMisspelledWord(nsAString& aNextMisspelledWord) {
   NS_ENSURE_TRUE(mSpellChecker, NS_ERROR_NOT_INITIALIZED);
 
   DeleteSuggestedWordList();
   // Beware! This may flush notifications via synchronous
   // ScrollSelectionIntoView.
-  return mSpellChecker->NextMisspelledWord(aNextMisspelledWord,
-                                           &mSuggestedWordList);
+  RefPtr<mozSpellChecker> spellChecker(mSpellChecker);
+  return spellChecker->NextMisspelledWord(aNextMisspelledWord,
+                                          &mSuggestedWordList);
 }
 
 NS_IMETHODIMP
 EditorSpellCheck::GetSuggestedWord(nsAString& aSuggestedWord) {
   // XXX This is buggy if mSuggestedWordList.Length() is over INT32_MAX.
   if (mSuggestedWordIndex < static_cast<int32_t>(mSuggestedWordList.Length())) {
     aSuggestedWord = mSuggestedWordList[mSuggestedWordIndex];
     mSuggestedWordIndex++;
@@ -445,17 +446,18 @@ RefPtr<CheckWordPromise> EditorSpellChec
 }
 
 NS_IMETHODIMP
 EditorSpellCheck::ReplaceWord(const nsAString& aMisspelledWord,
                               const nsAString& aReplaceWord,
                               bool aAllOccurrences) {
   NS_ENSURE_TRUE(mSpellChecker, NS_ERROR_NOT_INITIALIZED);
 
-  return mSpellChecker->Replace(aMisspelledWord, aReplaceWord, aAllOccurrences);
+  RefPtr<mozSpellChecker> spellChecker(mSpellChecker);
+  return spellChecker->Replace(aMisspelledWord, aReplaceWord, aAllOccurrences);
 }
 
 NS_IMETHODIMP
 EditorSpellCheck::IgnoreWordAllOccurrences(const nsAString& aWord) {
   NS_ENSURE_TRUE(mSpellChecker, NS_ERROR_NOT_INITIALIZED);
 
   return mSpellChecker->IgnoreAll(aWord);
 }
--- a/editor/spellchecker/TextServicesDocument.cpp
+++ b/editor/spellchecker/TextServicesDocument.cpp
@@ -1661,17 +1661,20 @@ bool TextServicesDocument::HasSameBlockN
 bool TextServicesDocument::IsTextNode(nsIContent* aContent) {
   NS_ENSURE_TRUE(aContent, false);
   return nsINode::TEXT_NODE == aContent->NodeType();
 }
 
 nsresult TextServicesDocument::SetSelectionInternal(int32_t aOffset,
                                                     int32_t aLength,
                                                     bool aDoUpdate) {
-  NS_ENSURE_TRUE(mSelCon && aOffset >= 0 && aLength >= 0, NS_ERROR_FAILURE);
+  if (NS_WARN_IF(!mSelCon) || NS_WARN_IF(aOffset < 0) ||
+      NS_WARN_IF(aLength < 0)) {
+    return NS_ERROR_INVALID_ARG;
+  }
 
   nsCOMPtr<nsINode> startNode;
   int32_t startNodeOffset = 0;
   OffsetEntry* entry;
 
   // Find start of selection in node offset terms:
 
   for (size_t i = 0; !startNode && i < mOffsetTable.Length(); i++) {
@@ -1723,36 +1726,32 @@ nsresult TextServicesDocument::SetSelect
   }
 
   NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE);
 
   // XXX: If we ever get a SetSelection() method in nsIEditor, we should
   //      use it.
 
   RefPtr<Selection> selection;
-
   if (aDoUpdate) {
     selection = mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL);
-    NS_ENSURE_STATE(selection);
-
-    nsresult rv = selection->Collapse(startNode, startNodeOffset);
-    NS_ENSURE_SUCCESS(rv, rv);
+    if (NS_WARN_IF(!selection)) {
+      return NS_ERROR_FAILURE;
+    }
   }
 
-  if (aLength <= 0) {
-    // We have a collapsed selection. (Caret)
-
+  if (!aLength) {
+    if (aDoUpdate) {
+      nsresult rv = selection->Collapse(startNode, startNodeOffset);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+    }
     mSelEndIndex = mSelStartIndex;
     mSelEndOffset = mSelStartOffset;
-
-    //**** KDEBUG ****
-    // printf("\n* Sel: (%2d, %4d) (%2d, %4d)\n", mSelStartIndex,
-    //        mSelStartOffset, mSelEndIndex, mSelEndOffset);
-    //**** KDEBUG ****
-
     return NS_OK;
   }
 
   // Find the end of the selection in node offset terms:
   nsCOMPtr<nsINode> endNode;
   int32_t endNodeOffset = 0;
   int32_t endOffset = aOffset + aLength;
   for (int32_t i = mOffsetTable.Length() - 1; !endNode && i >= 0; i--) {
@@ -1775,28 +1774,32 @@ nsresult TextServicesDocument::SetSelect
 
       if (endNode) {
         mSelEndIndex = i;
         mSelEndOffset = endOffset;
       }
     }
   }
 
-  if (aDoUpdate && endNode) {
-    nsresult rv = selection->Extend(endNode, endNodeOffset);
-
-    NS_ENSURE_SUCCESS(rv, rv);
+  if (!aDoUpdate) {
+    return NS_OK;
   }
 
-  //**** KDEBUG ****
-  // printf("\n * Sel: (%2d, %4d) (%2d, %4d)\n", mSelStartIndex,
-  //        mSelStartOffset, mSelEndIndex, mSelEndOffset);
-  //**** KDEBUG ****
-
-  return NS_OK;
+  if (!endNode) {
+    nsresult rv = selection->Collapse(startNode, startNodeOffset);
+    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to collapse selection");
+    return rv;
+  }
+
+  ErrorResult error;
+  selection->SetStartAndEndInLimiter(
+      RawRangeBoundary(startNode, startNodeOffset),
+      RawRangeBoundary(endNode, endNodeOffset), error);
+  NS_WARNING_ASSERTION(!error.Failed(), "Failed to set selection");
+  return error.StealNSResult();
 }
 
 nsresult TextServicesDocument::GetSelection(BlockSelectionStatus* aSelStatus,
                                             int32_t* aSelOffset,
                                             int32_t* aSelLength) {
   NS_ENSURE_TRUE(aSelStatus && aSelOffset && aSelLength, NS_ERROR_NULL_POINTER);
 
   *aSelStatus = BlockSelectionStatus::eBlockNotFound;
--- a/editor/spellchecker/TextServicesDocument.h
+++ b/editor/spellchecker/TextServicesDocument.h
@@ -145,16 +145,17 @@ class TextServicesDocument final : publi
    * @param aSelectionStatus    [OUT] This will contain the text block
    *                            selection status.
    * @param aSelectionOffset    [OUT] This will contain the offset into the
    *                            string returned by GetCurrentTextBlock() where
    *                            the selection begins.
    * @param aLength             [OUT] This will contain the number of
    *                            characters that are selected in the string.
    */
+  MOZ_CAN_RUN_SCRIPT
   nsresult LastSelectedBlock(BlockSelectionStatus* aSelStatus,
                              int32_t* aSelOffset, int32_t* aSelLength);
 
   /**
    * Tells the document to point to the text block before the current one.
    * This method will return NS_OK, even if there is no previous block.
    * Callers should call IsDone() to check if we have gone beyond the first
    * text block in the document.
@@ -184,34 +185,37 @@ class TextServicesDocument final : publi
    * into the string returned by GetCurrentTextBlock().  A length of zero
    * places the cursor at that offset. A positive non-zero length "n" selects
    * n characters in the string.
    *
    * @param aOffset             Offset into string returned by
    *                            GetCurrentTextBlock().
    * @param aLength             Number of characters selected.
    */
+  MOZ_CAN_RUN_SCRIPT
   nsresult SetSelection(int32_t aOffset, int32_t aLength);
 
   /**
    * Scrolls the document so that the current selection is visible.
    */
   nsresult ScrollSelectionIntoView();
 
   /**
    * Deletes the text selected by SetSelection(). Calling DeleteSelection()
    * with nothing selected, or with a collapsed selection (cursor) does
    * nothing and returns NS_OK.
    */
+  MOZ_CAN_RUN_SCRIPT
   nsresult DeleteSelection();
 
   /**
    * Inserts the given text at the current cursor position.  If there is a
    * selection, it will be deleted before the text is inserted.
    */
+  MOZ_CAN_RUN_SCRIPT
   nsresult InsertText(const nsString* aText);
 
   /**
    * nsIEditActionListener method implementations.
    */
   NS_DECL_NSIEDITACTIONLISTENER
 
   /**
@@ -262,20 +266,23 @@ class TextServicesDocument final : publi
   static bool IsTextNode(nsIContent* aContent);
 
   static bool DidSkip(FilteredContentIterator* aFilteredIter);
   static void ClearDidSkip(FilteredContentIterator* aFilteredIter);
 
   static bool HasSameBlockNodeParent(nsIContent* aContent1,
                                      nsIContent* aContent2);
 
+  MOZ_CAN_RUN_SCRIPT
   nsresult SetSelectionInternal(int32_t aOffset, int32_t aLength,
                                 bool aDoUpdate);
+  MOZ_CAN_RUN_SCRIPT
   nsresult GetSelection(BlockSelectionStatus* aSelStatus, int32_t* aSelOffset,
                         int32_t* aSelLength);
+  MOZ_CAN_RUN_SCRIPT
   nsresult GetCollapsedSelection(BlockSelectionStatus* aSelStatus,
                                  int32_t* aSelOffset, int32_t* aSelLength);
   nsresult GetUncollapsedSelection(BlockSelectionStatus* aSelStatus,
                                    int32_t* aSelOffset, int32_t* aSelLength);
 
   bool SelectionIsCollapsed();
   bool SelectionIsValid();
 
--- a/extensions/spellcheck/src/mozSpellChecker.cpp
+++ b/extensions/spellcheck/src/mozSpellChecker.cpp
@@ -95,17 +95,18 @@ nsresult mozSpellChecker::NextMisspelled
     do {
       result = mConverter->FindNextWord(str.get(), str.Length(), selOffset,
                                         &begin, &end);
       if (NS_SUCCEEDED(result) && begin != -1) {
         const nsAString &currWord = Substring(str, begin, end - begin);
         result = CheckWord(currWord, &isMisspelled, aSuggestions);
         if (isMisspelled) {
           aWord = currWord;
-          mTextServicesDocument->SetSelection(begin, end - begin);
+          MOZ_KnownLive(mTextServicesDocument)
+              ->SetSelection(begin, end - begin);
           // After ScrollSelectionIntoView(), the pending notifications might
           // be flushed and PresShell/PresContext/Frames may be dead.
           // See bug 418470.
           mTextServicesDocument->ScrollSelectionIntoView();
           return NS_OK;
         }
       }
       selOffset = end;
@@ -219,18 +220,19 @@ nsresult mozSpellChecker::Replace(const 
             // block move the selection point forwards
             if (currentBlock == startBlock && begin < selOffset) {
               selOffset +=
                   int32_t(aNewWord.Length()) - int32_t(aOldWord.Length());
               if (selOffset < begin) {
                 selOffset = begin;
               }
             }
-            mTextServicesDocument->SetSelection(begin, end - begin);
-            mTextServicesDocument->InsertText(&newWord);
+            MOZ_KnownLive(mTextServicesDocument)
+                ->SetSelection(begin, end - begin);
+            MOZ_KnownLive(mTextServicesDocument)->InsertText(&newWord);
             mTextServicesDocument->GetCurrentTextBlock(&str);
             end += (aNewWord.Length() -
                     aOldWord.Length());  // recursion was cute in GEB, not here.
           }
         }
         currOffset = end;
       } while (currOffset != -1);
       mTextServicesDocument->NextBlock();
@@ -262,23 +264,23 @@ nsresult mozSpellChecker::Replace(const 
       result = mConverter->FindNextWord(str.get(), str.Length(), selOffset,
                                         &begin, &end);
       if (end == -1) {
         mTextServicesDocument->NextBlock();
         selOffset = 0;
         result = mTextServicesDocument->GetCurrentTextBlock(&str);
         result = mConverter->FindNextWord(str.get(), str.Length(), selOffset,
                                           &begin, &end);
-        mTextServicesDocument->SetSelection(begin, 0);
+        MOZ_KnownLive(mTextServicesDocument)->SetSelection(begin, 0);
       } else {
-        mTextServicesDocument->SetSelection(begin, 0);
+        MOZ_KnownLive(mTextServicesDocument)->SetSelection(begin, 0);
       }
     }
   } else {
-    mTextServicesDocument->InsertText(&newWord);
+    MOZ_KnownLive(mTextServicesDocument)->InsertText(&newWord);
   }
   return NS_OK;
 }
 
 nsresult mozSpellChecker::IgnoreAll(const nsAString &aWord) {
   if (mPersonalDictionary) {
     mPersonalDictionary->IgnoreWord(aWord);
   }
@@ -456,18 +458,18 @@ nsresult mozSpellChecker::SetupDoc(int32
   nsresult rv;
 
   TextServicesDocument::BlockSelectionStatus blockStatus;
   int32_t selOffset;
   int32_t selLength;
   *outBlockOffset = 0;
 
   if (!mFromStart) {
-    rv = mTextServicesDocument->LastSelectedBlock(&blockStatus, &selOffset,
-                                                  &selLength);
+    rv = MOZ_KnownLive(mTextServicesDocument)
+             ->LastSelectedBlock(&blockStatus, &selOffset, &selLength);
     if (NS_SUCCEEDED(rv) &&
         blockStatus !=
             TextServicesDocument::BlockSelectionStatus::eBlockNotFound) {
       switch (blockStatus) {
         // No TB in S, but found one before/after S.
         case TextServicesDocument::BlockSelectionStatus::eBlockOutside:
         // S begins or ends in TB but extends outside of TB.
         case TextServicesDocument::BlockSelectionStatus::eBlockPartial:
--- a/extensions/spellcheck/src/mozSpellChecker.h
+++ b/extensions/spellcheck/src/mozSpellChecker.h
@@ -46,16 +46,17 @@ class mozSpellChecker final {
                        bool aFromStartofDoc);
 
   /**
    * Selects (hilites) the next misspelled word in the document.
    * @param aWord will contain the misspelled word.
    * @param aSuggestions is an array of nsStrings, that represent the
    * suggested replacements for the misspelled word.
    */
+  MOZ_CAN_RUN_SCRIPT
   nsresult NextMisspelledWord(nsAString& aWord,
                               nsTArray<nsString>* aSuggestions);
 
   /**
    * Checks if a word is misspelled. No document is required to use this method.
    * @param aWord is the word to check.
    * @param aIsMisspelled will be set to true if the word is misspelled.
    * @param aSuggestions is an array of nsStrings which represent the
@@ -76,16 +77,17 @@ class mozSpellChecker final {
   /**
    * Replaces the old word with the specified new word.
    * @param aOldWord is the word to be replaced.
    * @param aNewWord is the word that is to replace old word.
    * @param aAllOccurrences will replace all occurrences of old
    * word, in the document, with new word when it is true. If
    * false, it will replace the 1st occurrence only!
    */
+  MOZ_CAN_RUN_SCRIPT
   nsresult Replace(const nsAString& aOldWord, const nsAString& aNewWord,
                    bool aAllOccurrences);
 
   /**
    * Ignores all occurrences of the specified word in the document.
    * @param aWord is the word to ignore.
    */
   nsresult IgnoreAll(const nsAString& aWord);
@@ -157,16 +159,17 @@ class mozSpellChecker final {
   RefPtr<mozilla::TextServicesDocument> mTextServicesDocument;
   nsCOMPtr<mozIPersonalDictionary> mPersonalDictionary;
 
   nsCOMPtr<mozISpellCheckingEngine> mSpellCheckingEngine;
   bool mFromStart;
 
   nsString mCurrentDictionary;
 
+  MOZ_CAN_RUN_SCRIPT
   nsresult SetupDoc(int32_t* outBlockOffset);
 
   nsresult GetCurrentBlockIndex(
       mozilla::TextServicesDocument* aTextServicesDocument,
       int32_t* aOutBlockIndex);
 
   nsresult GetEngineList(nsCOMArray<mozISpellCheckingEngine>* aDictionaryList);