Bug 1444847 - part 7: Make `EditorSpellCheck` use `StaticRange` instead of `nsRange` to initialize itself r=smaug

`EditorSpellCheck` clones `nsRange` instance only for temporary use during
initialization even though the DOM tree won't be changed during it.  In this
case, using `StaticRange` is better since it does not need to observe the
DOM tree mutation.  I.e., we can save the cost of registering and
unregistering the mutation observer.

Differential Revision: https://phabricator.services.mozilla.com/D35146

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Masayuki Nakano 2019-06-28 07:50:36 +00:00
Родитель 4173161363
Коммит ccf6a4c660
5 изменённых файлов: 126 добавлений и 81 удалений

Просмотреть файл

@ -11,6 +11,7 @@
#include "mozilla/HTMLEditor.h" // for HTMLEditor
#include "mozilla/dom/Element.h" // for Element
#include "mozilla/dom/Selection.h"
#include "mozilla/dom/StaticRange.h"
#include "mozilla/intl/LocaleService.h" // for retrieving app locale
#include "mozilla/mozalloc.h" // for operator delete, etc
#include "mozilla/mozSpellChecker.h" // for mozSpellChecker
@ -369,19 +370,24 @@ EditorSpellCheck::InitSpellChecker(nsIEditor* aEditor,
if (!range->Collapsed()) {
// We don't want to touch the range in the selection,
// so create a new copy of it.
RefPtr<nsRange> rangeBounds = range->CloneRange();
RefPtr<StaticRange> staticRange =
StaticRange::Create(range, IgnoreErrors());
if (NS_WARN_IF(!staticRange)) {
return NS_ERROR_FAILURE;
}
// Make sure the new range spans complete words.
rv = textServicesDocument->ExpandRangeToWordBoundaries(rangeBounds);
NS_ENSURE_SUCCESS(rv, rv);
rv = textServicesDocument->ExpandRangeToWordBoundaries(staticRange);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// Now tell the text services that you only want
// to iterate over the text in this range.
rv = textServicesDocument->SetExtent(rangeBounds);
NS_ENSURE_SUCCESS(rv, rv);
rv = textServicesDocument->SetExtent(staticRange);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
}
}

Просмотреть файл

@ -8,6 +8,7 @@
#include "mozilla/ContentIterator.h"
#include "mozilla/Move.h"
#include "mozilla/mozalloc.h"
#include "mozilla/dom/AbstractRange.h"
#include "nsComponentManagerUtils.h"
#include "nsComposeTxtSrvFilter.h"
@ -23,6 +24,8 @@
namespace mozilla {
using namespace dom;
FilteredContentIterator::FilteredContentIterator(
UniquePtr<nsComposeTxtSrvFilter> aFilter)
: mCurrentIterator(nullptr),
@ -53,17 +56,19 @@ nsresult FilteredContentIterator::Init(nsINode* aRoot) {
return mPostIterator.Init(mRange);
}
nsresult FilteredContentIterator::Init(nsRange* aRange) {
if (NS_WARN_IF(!aRange)) {
nsresult FilteredContentIterator::Init(const AbstractRange* aAbstractRange) {
if (NS_WARN_IF(!aAbstractRange)) {
return NS_ERROR_INVALID_ARG;
}
if (NS_WARN_IF(!aRange->IsPositioned())) {
if (NS_WARN_IF(!aAbstractRange->IsPositioned())) {
return NS_ERROR_INVALID_ARG;
}
mRange = aRange->CloneRange();
mRange = nsRange::Create(aAbstractRange, IgnoreErrors());
if (NS_WARN_IF(!mRange)) {
return NS_ERROR_FAILURE;
}
return InitWithRange();
}

Просмотреть файл

@ -20,6 +20,10 @@ class nsRange;
namespace mozilla {
namespace dom {
class AbstractRange;
}
class FilteredContentIterator final {
public:
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(FilteredContentIterator)
@ -28,7 +32,7 @@ class FilteredContentIterator final {
explicit FilteredContentIterator(UniquePtr<nsComposeTxtSrvFilter> aFilter);
nsresult Init(nsINode* aRoot);
nsresult Init(nsRange* aRange);
nsresult Init(const dom::AbstractRange* aAbstractRange);
nsresult Init(nsINode* aStartContainer, uint32_t aStartOffset,
nsINode* aEndContainer, uint32_t aEndOffset);
nsresult Init(const RawRangeBoundary& aStartBoundary,

Просмотреть файл

@ -8,6 +8,7 @@
#include "FilteredContentIterator.h" // for FilteredContentIterator
#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
#include "mozilla/EditorUtils.h" // for AutoTransactionBatchExternal
#include "mozilla/dom/AbstractRange.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/Selection.h"
#include "mozilla/mozalloc.h" // for operator new, etc
@ -142,61 +143,66 @@ nsresult TextServicesDocument::InitWithEditor(nsIEditor* aEditor) {
return rv;
}
nsresult TextServicesDocument::SetExtent(nsRange* aRange) {
NS_ENSURE_ARG_POINTER(aRange);
NS_ENSURE_TRUE(mDocument, NS_ERROR_FAILURE);
nsresult TextServicesDocument::SetExtent(const AbstractRange* aAbstractRange) {
MOZ_ASSERT(aAbstractRange);
// We need to store a copy of aDOMRange since we don't
// know where it came from.
if (NS_WARN_IF(!mDocument)) {
return NS_ERROR_FAILURE;
}
mExtent = aRange->CloneRange();
// We need to store a copy of aAbstractRange since we don't know where it
// came from.
mExtent = nsRange::Create(aAbstractRange, IgnoreErrors());
if (NS_WARN_IF(!mExtent)) {
return NS_ERROR_FAILURE;
}
// Create a new iterator based on our new extent range.
nsresult rv =
CreateFilteredContentIterator(mExtent, getter_AddRefs(mFilteredIter));
if (NS_FAILED(rv)) {
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// Now position the iterator at the start of the first block
// in the range.
mIteratorStatus = IteratorStatus::eDone;
rv = FirstBlock();
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "FirstBlock() failed");
return rv;
}
nsresult TextServicesDocument::ExpandRangeToWordBoundaries(nsRange* aRange) {
NS_ENSURE_ARG_POINTER(aRange);
nsresult TextServicesDocument::ExpandRangeToWordBoundaries(
StaticRange* aStaticRange) {
MOZ_ASSERT(aStaticRange);
// Get the end points of the range.
nsCOMPtr<nsINode> rngStartNode, rngEndNode;
int32_t rngStartOffset, rngEndOffset;
nsresult rv =
GetRangeEndPoints(aRange, getter_AddRefs(rngStartNode), &rngStartOffset,
getter_AddRefs(rngEndNode), &rngEndOffset);
NS_ENSURE_SUCCESS(rv, rv);
nsresult rv = GetRangeEndPoints(aStaticRange, getter_AddRefs(rngStartNode),
&rngStartOffset, getter_AddRefs(rngEndNode),
&rngEndOffset);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// Create a content iterator based on the range.
RefPtr<FilteredContentIterator> filteredIter;
rv = CreateFilteredContentIterator(aRange, getter_AddRefs(filteredIter));
NS_ENSURE_SUCCESS(rv, rv);
rv =
CreateFilteredContentIterator(aStaticRange, getter_AddRefs(filteredIter));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// Find the first text node in the range.
IteratorStatus iterStatus = IteratorStatus::eDone;
rv = FirstTextNode(filteredIter, &iterStatus);
NS_ENSURE_SUCCESS(rv, rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (iterStatus == IteratorStatus::eDone) {
// No text was found so there's no adjustment necessary!
@ -204,12 +210,16 @@ nsresult TextServicesDocument::ExpandRangeToWordBoundaries(nsRange* aRange) {
}
nsINode* firstText = filteredIter->GetCurrentNode();
NS_ENSURE_TRUE(firstText, NS_ERROR_FAILURE);
if (NS_WARN_IF(!firstText)) {
return NS_ERROR_FAILURE;
}
// Find the last text node in the range.
rv = LastTextNode(filteredIter, &iterStatus);
NS_ENSURE_SUCCESS(rv, rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (iterStatus == IteratorStatus::eDone) {
// We should never get here because a first text block
@ -219,7 +229,9 @@ nsresult TextServicesDocument::ExpandRangeToWordBoundaries(nsRange* aRange) {
}
nsINode* lastText = filteredIter->GetCurrentNode();
NS_ENSURE_TRUE(lastText, NS_ERROR_FAILURE);
if (NS_WARN_IF(!lastText)) {
return NS_ERROR_FAILURE;
}
// Now make sure our end points are in terms of text nodes in the range!
@ -240,13 +252,16 @@ nsresult TextServicesDocument::ExpandRangeToWordBoundaries(nsRange* aRange) {
RefPtr<FilteredContentIterator> docFilteredIter;
rv = CreateDocumentContentIterator(getter_AddRefs(docFilteredIter));
NS_ENSURE_SUCCESS(rv, rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// Grab all the text in the block containing our
// first text node.
rv = docFilteredIter->PositionAt(firstText);
NS_ENSURE_SUCCESS(rv, rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
iterStatus = IteratorStatus::eValid;
@ -269,7 +284,9 @@ nsresult TextServicesDocument::ExpandRangeToWordBoundaries(nsRange* aRange) {
ClearOffsetTable(&offsetTable);
NS_ENSURE_SUCCESS(rv, rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rngStartNode = wordStartNode;
rngStartOffset = wordStartOffset;
@ -278,7 +295,9 @@ nsresult TextServicesDocument::ExpandRangeToWordBoundaries(nsRange* aRange) {
// last text node.
rv = docFilteredIter->PositionAt(lastText);
NS_ENSURE_SUCCESS(rv, rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
iterStatus = IteratorStatus::eValid;
@ -295,7 +314,9 @@ nsresult TextServicesDocument::ExpandRangeToWordBoundaries(nsRange* aRange) {
ClearOffsetTable(&offsetTable);
NS_ENSURE_SUCCESS(rv, rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// To prevent expanding the range too much, we only change
// rngEndNode and rngEndOffset if it isn't already at the start of the
@ -307,14 +328,11 @@ nsresult TextServicesDocument::ExpandRangeToWordBoundaries(nsRange* aRange) {
rngEndOffset = wordEndOffset;
}
// Now adjust the range so that it uses our new
// end points.
rv = aRange->SetStartAndEnd(rngStartNode, rngStartOffset, rngEndNode,
rngEndOffset);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
// Now adjust the range so that it uses our new end points.
rv = aStaticRange->SetStartAndEnd(rngStartNode, rngStartOffset, rngEndNode,
rngEndOffset);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to update the given range");
return rv;
}
nsresult TextServicesDocument::SetFilterType(uint32_t aFilterType) {
@ -1367,8 +1385,11 @@ void TextServicesDocument::DidJoinNodes(nsINode& aLeftNode,
}
nsresult TextServicesDocument::CreateFilteredContentIterator(
nsRange* aRange, FilteredContentIterator** aFilteredIter) {
NS_ENSURE_TRUE(aRange && aFilteredIter, NS_ERROR_NULL_POINTER);
const AbstractRange* aAbstractRange,
FilteredContentIterator** aFilteredIter) {
if (NS_WARN_IF(!aAbstractRange) || NS_WARN_IF(!aFilteredIter)) {
return NS_ERROR_INVALID_ARG;
}
*aFilteredIter = nullptr;
@ -1388,7 +1409,7 @@ nsresult TextServicesDocument::CreateFilteredContentIterator(
RefPtr<FilteredContentIterator> filter =
new FilteredContentIterator(std::move(composeFilter));
nsresult rv = filter->Init(aRange);
nsresult rv = filter->Init(aAbstractRange);
if (NS_FAILED(rv)) {
return rv;
}
@ -2250,24 +2271,27 @@ bool TextServicesDocument::SelectionIsCollapsed() {
bool TextServicesDocument::SelectionIsValid() { return mSelStartIndex >= 0; }
// static
nsresult TextServicesDocument::GetRangeEndPoints(nsRange* aRange,
nsINode** aStartContainer,
int32_t* aStartOffset,
nsINode** aEndContainer,
int32_t* aEndOffset) {
NS_ENSURE_TRUE(
aRange && aStartContainer && aStartOffset && aEndContainer && aEndOffset,
NS_ERROR_NULL_POINTER);
nsresult TextServicesDocument::GetRangeEndPoints(
const AbstractRange* aAbstractRange, nsINode** aStartContainer,
int32_t* aStartOffset, nsINode** aEndContainer, int32_t* aEndOffset) {
if (NS_WARN_IF(!aAbstractRange) || NS_WARN_IF(!aStartContainer) ||
NS_WARN_IF(!aEndContainer) || NS_WARN_IF(!aEndOffset)) {
return NS_ERROR_INVALID_ARG;
}
NS_IF_ADDREF(*aStartContainer = aRange->GetStartContainer());
NS_ENSURE_TRUE(aStartContainer, NS_ERROR_FAILURE);
nsCOMPtr<nsINode> startContainer = aAbstractRange->GetStartContainer();
if (NS_WARN_IF(!startContainer)) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsINode> endContainer = aAbstractRange->GetEndContainer();
if (NS_WARN_IF(!endContainer)) {
return NS_ERROR_FAILURE;
}
*aStartOffset = static_cast<int32_t>(aRange->StartOffset());
NS_IF_ADDREF(*aEndContainer = aRange->GetEndContainer());
NS_ENSURE_TRUE(aEndContainer, NS_ERROR_FAILURE);
*aEndOffset = static_cast<int32_t>(aRange->EndOffset());
startContainer.forget(aStartContainer);
endContainer.forget(aEndContainer);
*aStartOffset = static_cast<int32_t>(aAbstractRange->StartOffset());
*aEndOffset = static_cast<int32_t>(aAbstractRange->EndOffset());
return NS_OK;
}

Просмотреть файл

@ -27,8 +27,10 @@ class OffsetEntry;
class TextEditor;
namespace dom {
class AbstractRange;
class Document;
class Element;
class StaticRange;
}; // namespace dom
/**
@ -89,18 +91,18 @@ class TextServicesDocument final : public nsIEditActionListener {
* method. If this method is never called, the text services defaults to
* iterating over the entire document.
*
* @param aDOMRange The range to use. aDOMRange must point to a
* @param aAbstractRange The range to use. aAbstractRange must point to a
* valid range object.
*/
nsresult SetExtent(nsRange* aRange);
nsresult SetExtent(const dom::AbstractRange* aAbstractRange);
/**
* Expands the end points of the range so that it spans complete words. This
* call does not change any internal state of the text services document.
*
* @param aDOMRange The range to be expanded/adjusted.
* @param aStaticRange [in/out] The range to be expanded/adjusted.
*/
nsresult ExpandRangeToWordBoundaries(nsRange* aRange);
nsresult ExpandRangeToWordBoundaries(dom::StaticRange* aStaticRange);
/**
* Sets the filter type to be used while iterating over content.
@ -229,14 +231,18 @@ class TextServicesDocument final : public nsIEditActionListener {
void DidDeleteNode(nsINode* aChild);
void DidJoinNodes(nsINode& aLeftNode, nsINode& aRightNode);
static nsresult GetRangeEndPoints(nsRange* aRange, nsINode** aStartContainer,
// TODO: We should get rid of this method since `aAbstractRange` has
// enough simple API to get them.
static nsresult GetRangeEndPoints(const dom::AbstractRange* aAbstractRange,
nsINode** aStartContainer,
int32_t* aStartOffset,
nsINode** aEndContainer,
int32_t* aEndOffset);
private:
nsresult CreateFilteredContentIterator(
nsRange* aRange, FilteredContentIterator** aFilteredIter);
const dom::AbstractRange* aAbstractRange,
FilteredContentIterator** aFilteredIter);
dom::Element* GetDocumentContentRootNode() const;
already_AddRefed<nsRange> CreateDocumentContentRange();