зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
4173161363
Коммит
ccf6a4c660
|
@ -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();
|
||||
|
|
Загрузка…
Ссылка в новой задаче