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

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Masayuki Nakano 2019-03-18 01:52:36 +00:00
parent 448571fd81
commit 6dd0ecdd8e
22 changed files with 179 additions and 134 deletions

View file

@ -2981,32 +2981,28 @@ nsresult ContentEventHandler::OnSelectionEvent(WidgetSelectionEvent* aEvent) {
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);

View file

@ -123,6 +123,7 @@ class MOZ_STACK_CLASS ContentEventHandler {
nsresult OnQueryDOMWidgetHittest(WidgetQueryContentEvent* aEvent);
// NS_SELECTION_* event
MOZ_CAN_RUN_SCRIPT
nsresult OnSelectionEvent(WidgetSelectionEvent* aEvent);
protected:

View file

@ -750,10 +750,12 @@ nsresult EventStateManager::PreHandleEvent(nsPresContext* aPresContext,
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:

View file

@ -205,6 +205,7 @@ class IMEStateManager {
* 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);

View file

@ -456,9 +456,13 @@ class TextComposition final {
* 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);

View file

@ -389,6 +389,7 @@ class EditorBase : public nsIEditor,
return mTransactionManager->RemoveTransactionListener(aListener);
}
MOZ_CAN_RUN_SCRIPT
virtual nsresult HandleKeyPressEvent(WidgetKeyboardEvent* aKeyboardEvent);
virtual dom::EventTarget* GetDOMEventTarget() = 0;

View file

@ -1731,7 +1731,8 @@ EditActionResult HTMLEditRules::WillInsertParagraphSeparator() {
// 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);
@ -2221,7 +2222,8 @@ nsresult HTMLEditRules::WillDeleteSelection(
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;
}
@ -6508,13 +6510,6 @@ nsresult HTMLEditRules::ExpandSelectionForDeletion() {
}
}
}
// 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).
@ -6542,26 +6537,20 @@ nsresult HTMLEditRules::ExpandSelectionForDeletion() {
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() {
@ -6714,20 +6703,18 @@ nsresult HTMLEditRules::NormalizeSelection() {
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,

View file

@ -230,6 +230,7 @@ class HTMLEditRules : public TextEditRules {
* @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);
@ -374,6 +375,7 @@ class HTMLEditRules : public TextEditRules {
/**
* XXX Should document what this does.
*/
MOZ_CAN_RUN_SCRIPT
MOZ_MUST_USE nsresult WillMakeList(const nsAString* aListType,
bool aEntireList,
const nsAString* aBulletType,
@ -388,6 +390,7 @@ class HTMLEditRules : public TextEditRules {
* @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);
/**
@ -397,6 +400,7 @@ class HTMLEditRules : public TextEditRules {
* @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);
/**
@ -406,6 +410,7 @@ class HTMLEditRules : public TextEditRules {
* @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);
/**
@ -415,6 +420,7 @@ class HTMLEditRules : public TextEditRules {
* @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);
/**
@ -424,6 +430,7 @@ class HTMLEditRules : public TextEditRules {
* @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);
/**
@ -435,6 +442,7 @@ class HTMLEditRules : public TextEditRules {
* @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);
@ -473,6 +481,7 @@ class HTMLEditRules : public TextEditRules {
* @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);
@ -485,6 +494,7 @@ class HTMLEditRules : public TextEditRules {
* @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);
@ -501,6 +511,7 @@ class HTMLEditRules : public TextEditRules {
* will be called.
* Otherwise, ApplyBlockStyle() will be called.
*/
MOZ_CAN_RUN_SCRIPT
MOZ_MUST_USE nsresult MakeBasicBlock(nsAtom& aBlockType);
/**
@ -519,6 +530,7 @@ class HTMLEditRules : public TextEditRules {
* @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);
/**
@ -848,6 +860,7 @@ class HTMLEditRules : public TextEditRules {
* invisible <br> element for preventing delete action handler to keep
* unexpected nodes.
*/
MOZ_CAN_RUN_SCRIPT
MOZ_MUST_USE nsresult ExpandSelectionForDeletion();
/**
@ -856,6 +869,7 @@ class HTMLEditRules : public TextEditRules {
* 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();
/**

View file

@ -1727,7 +1727,7 @@ HTMLEditor::SelectElement(Element* aElement) {
return NS_ERROR_NOT_INITIALIZED;
}
nsresult rv = SelectContentInternal(*aElement);
nsresult rv = SelectContentInternal(MOZ_KnownLive(*aElement));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -1742,28 +1742,18 @@ nsresult HTMLEditor::SelectContentInternal(nsIContent& 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

View file

@ -141,6 +141,7 @@ class HTMLEditor final : public TextEditor,
NS_IMETHOD InsertLineBreak() override;
MOZ_CAN_RUN_SCRIPT
virtual nsresult HandleKeyPressEvent(
WidgetKeyboardEvent* aKeyboardEvent) override;
virtual nsIContent* GetFocusedContent() override;
@ -423,6 +424,7 @@ class HTMLEditor final : public TextEditor,
* 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);
/**
@ -641,6 +643,7 @@ class HTMLEditor final : public TextEditor,
* 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,
@ -964,6 +967,7 @@ class HTMLEditor final : public TextEditor,
*
* @param aContentToSelect The content which should be selected.
*/
MOZ_CAN_RUN_SCRIPT
nsresult SelectContentInternal(nsIContent& aContentToSelect);
/**
@ -1665,6 +1669,7 @@ class HTMLEditor final : public TextEditor,
*/
bool SetCaretInTableCell(dom::Element* aElement);
MOZ_CAN_RUN_SCRIPT
nsresult TabInTable(bool inIsShift, bool* outHandled);
/**
@ -1696,6 +1701,7 @@ class HTMLEditor final : public TextEditor,
* @param aInsertPosition Before or after the target cell which
* contains first selection range.
*/
MOZ_CAN_RUN_SCRIPT
nsresult InsertTableCellsWithTransaction(int32_t aNumberOfCellsToInsert,
InsertPosition aInsertPosition);
@ -1711,6 +1717,7 @@ class HTMLEditor final : public TextEditor,
* @param aInsertPosition Before or after the target cell which
* contains first selection range.
*/
MOZ_CAN_RUN_SCRIPT
nsresult InsertTableColumnsWithTransaction(int32_t aNumberOfColumnsToInsert,
InsertPosition aInsertPosition);
@ -1726,6 +1733,7 @@ class HTMLEditor final : public TextEditor,
* @param aInsertPosition Before or after the target cell which
* contains first selection range.
*/
MOZ_CAN_RUN_SCRIPT
nsresult InsertTableRowsWithTransaction(int32_t aNumberOfRowsToInsert,
InsertPosition aInsertPosition);
@ -1755,6 +1763,7 @@ class HTMLEditor final : public TextEditor,
* ignored if 2 ore more cells are
* selected.
*/
MOZ_CAN_RUN_SCRIPT
nsresult DeleteSelectedTableColumnsWithTransaction(
int32_t aNumberOfColumnsToDelete);
@ -1770,6 +1779,7 @@ class HTMLEditor final : public TextEditor,
* @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);
@ -1788,6 +1798,7 @@ class HTMLEditor final : public TextEditor,
* @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);
@ -1802,6 +1813,7 @@ class HTMLEditor final : public TextEditor,
* @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);
@ -1819,6 +1831,7 @@ class HTMLEditor final : public TextEditor,
* @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);
/**
@ -2122,6 +2135,7 @@ class HTMLEditor final : public TextEditor,
* 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);

View file

@ -258,7 +258,7 @@ nsresult HTMLEditorEventListener::MouseDown(MouseEvent* aMouseEvent) {
WidgetMouseEvent* mousedownEvent =
aMouseEvent->WidgetEventPtr()->AsMouseEvent();
HTMLEditor* htmlEditor = mEditorBase->AsHTMLEditor();
RefPtr<HTMLEditor> htmlEditor = mEditorBase->AsHTMLEditor();
MOZ_ASSERT(htmlEditor);
// Contenteditable should disregard mousedowns outside it.

View file

@ -63,10 +63,12 @@ class MOZ_STACK_CLASS AutoSelectionSetterAfterTableEdit final {
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);
}
}

View file

@ -1705,20 +1705,14 @@ nsresult TextEditRules::HideLastPasswordInputInternal() {
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;
}

View file

@ -105,6 +105,7 @@ class TextEditor : public EditorBase, public nsIPlaintextEditor {
return NS_SUCCEEDED(rv) && isEmpty;
}
MOZ_CAN_RUN_SCRIPT
virtual nsresult HandleKeyPressEvent(
WidgetKeyboardEvent* aKeyboardEvent) override;

View file

@ -36,6 +36,7 @@ interface nsIEditorSpellCheck : nsISupports
*
* @see mozSpellChecker::GetNextMisspelledWord
*/
[can_run_script]
AString GetNextMisspelledWord();
/**
@ -64,6 +65,7 @@ interface nsIEditorSpellCheck : nsISupports
*
* @see mozSpellChecker::CheckCurrentWord
*/
[can_run_script]
void ReplaceWord(in AString misspelledWord, in AString replaceWord, in boolean allOccurrences);
/**

View file

@ -183,6 +183,7 @@ interface nsIHTMLEditor : nsISupports
*
* @param aElement An element in the document
*/
[can_run_script]
void selectElement(in Element aElement);
/**

View file

@ -37,6 +37,7 @@ interface nsITableEditor : nsISupports
* before current cell. Otherwise, will
* be inserted after the cell.
*/
[can_run_script]
void insertTableCell(in long aNumberOfColumnsToInsert,
in boolean aInsertAfterSelectedCell);
@ -52,6 +53,7 @@ interface nsITableEditor : nsISupports
* before current cell. Otherwise, will
* be inserted after the cell.
*/
[can_run_script]
void insertTableColumn(in long aNumberOfColumnsToInsert,
in boolean aInsertAfterSelectedCell);
@ -67,6 +69,7 @@ interface nsITableEditor : nsISupports
* before current cell. Otherwise, will
* be inserted after the cell.
*/
[can_run_script]
void insertTableRow(in long aNumberOfRowsToInsert,
in boolean aInsertAfterSelectedCell);
@ -80,6 +83,7 @@ interface nsITableEditor : nsISupports
*
* @param aNumber Number of items to insert/delete
*/
[can_run_script]
void deleteTable();
/**
@ -89,6 +93,7 @@ interface nsITableEditor : nsISupports
* first selection range. This does nothing without exception if selection
* is not in cell element.
*/
[can_run_script]
void deleteTableCellContents();
/**
@ -105,6 +110,7 @@ interface nsITableEditor : nsISupports
* @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);
/**
@ -124,6 +130,7 @@ interface nsITableEditor : nsISupports
* ignored if 2 ore more cells are
* selected.
*/
[can_run_script]
void deleteTableColumn(in long aNumberOfColumnsToDelete);
/**
@ -141,12 +148,14 @@ interface nsITableEditor : nsISupports
* @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:
@ -157,12 +166,17 @@ interface nsITableEditor : nsISupports
* @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
@ -173,6 +187,7 @@ interface nsITableEditor : nsISupports
* @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
@ -195,6 +210,7 @@ interface nsITableEditor : nsISupports
* 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
@ -203,6 +219,7 @@ interface nsITableEditor : nsISupports
* 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
@ -215,6 +232,7 @@ interface nsITableEditor : nsISupports
* thus it can be used to fixup all tables
* in a page independent of the selection
*/
[can_run_script]
void normalizeTable(in Element aTable);
/**

View file

@ -407,8 +407,9 @@ EditorSpellCheck::GetNextMisspelledWord(nsAString& aNextMisspelledWord) {
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
@ -450,7 +451,8 @@ EditorSpellCheck::ReplaceWord(const nsAString& aMisspelledWord,
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

View file

@ -1666,7 +1666,10 @@ bool TextServicesDocument::IsTextNode(nsIContent* aContent) {
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;
@ -1728,26 +1731,22 @@ nsresult TextServicesDocument::SetSelectionInternal(int32_t aOffset,
// 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;
}
@ -1780,18 +1779,22 @@ nsresult TextServicesDocument::SetSelectionInternal(int32_t aOffset,
}
}
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 ****
if (!endNode) {
nsresult rv = selection->Collapse(startNode, startNodeOffset);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to collapse selection");
return rv;
}
return NS_OK;
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,

View file

@ -150,6 +150,7 @@ class TextServicesDocument final : public nsIEditActionListener {
* @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);
@ -189,6 +190,7 @@ class TextServicesDocument final : public nsIEditActionListener {
* GetCurrentTextBlock().
* @param aLength Number of characters selected.
*/
MOZ_CAN_RUN_SCRIPT
nsresult SetSelection(int32_t aOffset, int32_t aLength);
/**
@ -201,12 +203,14 @@ class TextServicesDocument final : public nsIEditActionListener {
* 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);
/**
@ -267,10 +271,13 @@ class TextServicesDocument final : public nsIEditActionListener {
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,

View file

@ -100,7 +100,8 @@ nsresult mozSpellChecker::NextMisspelledWord(nsAString &aWord,
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.
@ -224,8 +225,9 @@ nsresult mozSpellChecker::Replace(const nsAString &aOldWord,
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.
@ -267,13 +269,13 @@ nsresult mozSpellChecker::Replace(const nsAString &aOldWord,
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;
}
@ -461,8 +463,8 @@ nsresult mozSpellChecker::SetupDoc(int32_t *outBlockOffset) {
*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) {

View file

@ -51,6 +51,7 @@ class mozSpellChecker final {
* @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);
@ -81,6 +82,7 @@ class mozSpellChecker final {
* 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);
@ -162,6 +164,7 @@ class mozSpellChecker final {
nsString mCurrentDictionary;
MOZ_CAN_RUN_SCRIPT
nsresult SetupDoc(int32_t* outBlockOffset);
nsresult GetCurrentBlockIndex(