Backed out 4 changesets (bug 1898321, bug 1897942, bug 1897956) for causing multiple failures.
Backed out changeset ec3fcdcbfbd7 (bug 1897942) Backed out changeset 279025d15551 (bug 1898321) Backed out changeset a239855b02ea (bug 1897942) Backed out changeset aab98ffe0ee9 (bug 1897956)
This commit is contained in:
@@ -10736,12 +10736,11 @@ nsresult nsDocShell::ScrollToAnchor(bool aCurHasRef, bool aNewHasRef,
|
||||
// `Document::ScrollToRef()` is (presumably) the second "async" call mentioned
|
||||
// in sec. 7.4.2.3.3 in the HTML spec, "Fragment navigations":
|
||||
// https://html.spec.whatwg.org/#scroll-to-fragid:~:text=This%20algorithm%20will%20be%20called%20twice
|
||||
|
||||
const RefPtr fragmentDirective = GetDocument()->FragmentDirective();
|
||||
const nsTArray<RefPtr<nsRange>> textDirectives =
|
||||
fragmentDirective->FindTextFragmentsInDocument();
|
||||
const bool hasTextDirectives = !textDirectives.IsEmpty();
|
||||
fragmentDirective->HighlightTextDirectives(textDirectives);
|
||||
const bool hasScrolledToTextFragment =
|
||||
presShell->HighlightAndGoToTextFragment(scroll);
|
||||
if (hasScrolledToTextFragment) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// If we have no new anchor, we do not want to scroll, unless there is a
|
||||
// current anchor and we are doing a history load. So return if we have no
|
||||
@@ -10754,12 +10753,12 @@ nsresult nsDocShell::ScrollToAnchor(bool aCurHasRef, bool aNewHasRef,
|
||||
// Both the new and current URIs refer to the same page. We can now
|
||||
// browse to the hash stored in the new URI.
|
||||
|
||||
if (aNewHash.IsEmpty() && !hasTextDirectives) {
|
||||
if (aNewHash.IsEmpty()) {
|
||||
// 2. If fragment is the empty string, then return the special value top of
|
||||
// the document.
|
||||
//
|
||||
// Tell the shell it's at an anchor without scrolling.
|
||||
presShell->GoToAnchor(u""_ns, nullptr, false);
|
||||
presShell->GoToAnchor(u""_ns, false);
|
||||
|
||||
if (scroll) {
|
||||
// Scroll to the top of the page. Ignore the return value; failure to
|
||||
@@ -10774,10 +10773,7 @@ nsresult nsDocShell::ScrollToAnchor(bool aCurHasRef, bool aNewHasRef,
|
||||
// 3. Let potentialIndicatedElement be the result of finding a potential
|
||||
// indicated element given document and fragment.
|
||||
NS_ConvertUTF8toUTF16 uStr(aNewHash);
|
||||
RefPtr<nsRange> range =
|
||||
!textDirectives.IsEmpty() ? textDirectives[0] : nullptr;
|
||||
auto rv =
|
||||
presShell->GoToAnchor(uStr, range, scroll, ScrollFlags::ScrollSmoothAuto);
|
||||
auto rv = presShell->GoToAnchor(uStr, scroll, ScrollFlags::ScrollSmoothAuto);
|
||||
|
||||
// 4. If potentialIndicatedElement is not null, then return
|
||||
// potentialIndicatedElement.
|
||||
@@ -10798,7 +10794,7 @@ nsresult nsDocShell::ScrollToAnchor(bool aCurHasRef, bool aNewHasRef,
|
||||
if (fragmentBytes.IsEmpty()) {
|
||||
// When aNewHash contains "%00", the unescaped string may be empty, and
|
||||
// GoToAnchor asserts if we ask it to scroll to an empty ref.
|
||||
presShell->GoToAnchor(u""_ns, nullptr, false);
|
||||
presShell->GoToAnchor(u""_ns, false);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@@ -10815,8 +10811,7 @@ nsresult nsDocShell::ScrollToAnchor(bool aCurHasRef, bool aNewHasRef,
|
||||
// there is no such anchor in the document, which is actually a success
|
||||
// condition for us (we want to update the session history with the new URI no
|
||||
// matter whether we actually scrolled somewhere).
|
||||
presShell->GoToAnchor(decodedFragment, nullptr, scroll,
|
||||
ScrollFlags::ScrollSmoothAuto);
|
||||
presShell->GoToAnchor(decodedFragment, scroll, ScrollFlags::ScrollSmoothAuto);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@@ -13089,49 +13089,31 @@ void Document::ScrollToRef() {
|
||||
if (!presShell) {
|
||||
return;
|
||||
}
|
||||
|
||||
// XXX(:jjaschke): Document policy integration should happen here
|
||||
// as soon as https://bugzil.la/1860915 lands.
|
||||
// XXX(:jjaschke): Same goes for User Activation and security aspects,
|
||||
// tracked in https://bugzil.la/1888756.
|
||||
|
||||
// https://wicg.github.io/scroll-to-text-fragment/#invoking-text-directives
|
||||
// Monkeypatching HTML § 7.4.6.3 Scrolling to a fragment:
|
||||
// 1. Let text directives be the document's pending text directives.
|
||||
const RefPtr fragmentDirective = FragmentDirective();
|
||||
const nsTArray<RefPtr<nsRange>> textDirectives =
|
||||
fragmentDirective->FindTextFragmentsInDocument();
|
||||
// 2. If ranges is non-empty, then:
|
||||
// 2.1 Let firstRange be the first item of ranges
|
||||
RefPtr<nsRange> firstRange =
|
||||
!textDirectives.IsEmpty() ? textDirectives.ElementAt(0) : nullptr;
|
||||
// 2.2 Visually indicate each range in ranges in an implementation-defined
|
||||
// way. The indication must not be observable from author script. See § 3.7
|
||||
// Indicating The Text Match.
|
||||
fragmentDirective->HighlightTextDirectives(textDirectives);
|
||||
|
||||
// In a subsequent call to `ScrollToRef()` during page load, `textDirectives`
|
||||
// would only contain text directives that were not found in the previous
|
||||
// runs. If an earlier call during the same page load already found a text
|
||||
// directive to scroll to, only highlighting of the text directives needs to
|
||||
// be done.
|
||||
// This is indicated by `mScrolledToRefAlready`.
|
||||
if (mScrolledToRefAlready) {
|
||||
presShell->ScrollToAnchor();
|
||||
return;
|
||||
}
|
||||
|
||||
// If text directives is non-null, then highlight the text directives and
|
||||
// scroll to the last one.
|
||||
// XXX(:jjaschke): Document policy integration should happen here
|
||||
// as soon as https://bugzil.la/1860915 lands.
|
||||
// XXX(:jjaschke): Same goes for User Activation and security aspects,
|
||||
// tracked in https://bugzil.la/1888756.
|
||||
const bool didScrollToTextFragment =
|
||||
presShell->HighlightAndGoToTextFragment(true);
|
||||
|
||||
FragmentDirective()->ClearUninvokedDirectives();
|
||||
|
||||
// 2. If fragment is the empty string and no text directives have been
|
||||
// scrolled to, then return the special value top of the document.
|
||||
if (textDirectives.IsEmpty() && mScrollToRef.IsEmpty()) {
|
||||
if (didScrollToTextFragment || mScrollToRef.IsEmpty()) {
|
||||
return;
|
||||
}
|
||||
// 3. Let potentialIndicatedElement be the result of finding a potential
|
||||
// indicated element given document and fragment.
|
||||
NS_ConvertUTF8toUTF16 ref(mScrollToRef);
|
||||
// This also covers 2.3 of the Monkeypatch for text fragments mentioned above:
|
||||
// 2.3 Set firstRange as document's indicated part, return.
|
||||
auto rv = presShell->GoToAnchor(ref, firstRange,
|
||||
mChangeScrollPosWhenScrollingToRef);
|
||||
auto rv = presShell->GoToAnchor(ref, mChangeScrollPosWhenScrollingToRef);
|
||||
|
||||
// 4. If potentialIndicatedElement is not null, then return
|
||||
// potentialIndicatedElement.
|
||||
@@ -13159,7 +13141,7 @@ void Document::ScrollToRef() {
|
||||
|
||||
// 7. Set potentialIndicatedElement to the result of finding a potential
|
||||
// indicated element given document and decodedFragment.
|
||||
rv = presShell->GoToAnchor(decodedFragment, nullptr,
|
||||
rv = presShell->GoToAnchor(decodedFragment,
|
||||
mChangeScrollPosWhenScrollingToRef);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
mScrolledToRefAlready = true;
|
||||
|
||||
@@ -12,14 +12,11 @@
|
||||
#include "mozilla/dom/FragmentDirectiveBinding.h"
|
||||
#include "mozilla/dom/FragmentOrElement.h"
|
||||
#include "mozilla/dom/NodeBinding.h"
|
||||
#include "mozilla/dom/Selection.h"
|
||||
#include "mozilla/dom/Text.h"
|
||||
#include "mozilla/intl/WordBreaker.h"
|
||||
#include "mozilla/PresShell.h"
|
||||
#include "nsComputedDOMStyle.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsDOMAttributeMap.h"
|
||||
#include "nsFind.h"
|
||||
#include "nsGkAtoms.h"
|
||||
#include "nsICSSDeclaration.h"
|
||||
#include "nsIFrame.h"
|
||||
@@ -31,10 +28,6 @@
|
||||
namespace mozilla::dom {
|
||||
static LazyLogModule sFragmentDirectiveLog("FragmentDirective");
|
||||
|
||||
#define DBG(msg, ...) \
|
||||
MOZ_LOG(sFragmentDirectiveLog, LogLevel::Debug, \
|
||||
("%s(): " msg, __FUNCTION__, ##__VA_ARGS__))
|
||||
|
||||
/** Converts a `TextDirective` into a percent-encoded string. */
|
||||
nsCString ToString(const TextDirective& aTextDirective) {
|
||||
nsCString str;
|
||||
@@ -98,63 +91,15 @@ void FragmentDirective::ParseAndRemoveFragmentDirectiveFromFragment(
|
||||
nsTArray<RefPtr<nsRange>> FragmentDirective::FindTextFragmentsInDocument() {
|
||||
MOZ_ASSERT(mDocument);
|
||||
mDocument->FlushPendingNotifications(FlushType::Frames);
|
||||
// https://wicg.github.io/scroll-to-text-fragment/#invoke-text-directives
|
||||
// To invoke text directives, given as input a list of text directives text
|
||||
// directives and a Document document, run these steps:
|
||||
// 1. Let ranges be a list of ranges, initially empty.
|
||||
nsTArray<RefPtr<nsRange>> textDirectiveRanges;
|
||||
|
||||
// Additionally (not mentioned in the spec), remove all text directives from
|
||||
// the input list to keep only the ones that are not found.
|
||||
// This code runs repeatedly during a page load, so it is possible that the
|
||||
// match for a text directive has not been parsed yet.
|
||||
nsTArray<TextDirective> uninvokedTextDirectives(
|
||||
mUninvokedTextDirectives.Length());
|
||||
|
||||
// 2. For each text directive directive of text directives:
|
||||
for (TextDirective& textDirective : mUninvokedTextDirectives) {
|
||||
// 2.1 If the result of running find a range from a text directive given
|
||||
// directive and document is non-null, then append it to ranges.
|
||||
for (const TextDirective& textDirective : mUninvokedTextDirectives) {
|
||||
if (RefPtr<nsRange> range = FindRangeForTextDirective(textDirective)) {
|
||||
textDirectiveRanges.AppendElement(range);
|
||||
} else {
|
||||
uninvokedTextDirectives.AppendElement(std::move(textDirective));
|
||||
}
|
||||
}
|
||||
mUninvokedTextDirectives = std::move(uninvokedTextDirectives);
|
||||
|
||||
// 3. Return ranges.
|
||||
return textDirectiveRanges;
|
||||
}
|
||||
|
||||
void FragmentDirective::HighlightTextDirectives(
|
||||
const nsTArray<RefPtr<nsRange>>& aTextDirectiveRanges) {
|
||||
MOZ_ASSERT(mDocument);
|
||||
if (aTextDirectiveRanges.IsEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (!StaticPrefs::dom_text_fragments_enabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const RefPtr<Selection> targetTextSelection =
|
||||
[doc = this->mDocument]() -> Selection* {
|
||||
if (auto* presShell = doc->GetPresShell()) {
|
||||
return presShell->GetCurrentSelection(SelectionType::eTargetText);
|
||||
}
|
||||
return nullptr;
|
||||
}();
|
||||
if (!targetTextSelection) {
|
||||
return;
|
||||
}
|
||||
for (const RefPtr<nsRange>& range : aTextDirectiveRanges) {
|
||||
// Script won't be able to manipulate `aTextDirectiveRanges`,
|
||||
// therefore we can mark `range` as known live.
|
||||
targetTextSelection->AddRangeAndSelectFramesAndNotifyListeners(
|
||||
MOZ_KnownLive(*range), IgnoreErrors());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Determine if `aNode` should be considered when traversing the DOM.
|
||||
*
|
||||
@@ -446,7 +391,9 @@ RangeBoundary MoveRangeBoundaryOneWord(const RangeBoundary& aRangeBoundary,
|
||||
|
||||
RefPtr<nsRange> FragmentDirective::FindRangeForTextDirective(
|
||||
const TextDirective& aTextDirective) {
|
||||
DBG("Find range for text directive '%s'.", ToString(aTextDirective).Data());
|
||||
MOZ_LOG(sFragmentDirectiveLog, LogLevel::Info,
|
||||
("FragmentDirective::%s(): Find range for text directive '%s'.",
|
||||
__FUNCTION__, ToString(aTextDirective).Data()));
|
||||
// 1. Let searchRange be a range with start (document, 0) and end (document,
|
||||
// document’s length)
|
||||
ErrorResult rv;
|
||||
@@ -468,14 +415,8 @@ RefPtr<nsRange> FragmentDirective::FindRangeForTextDirective(
|
||||
FindStringInRange(searchRange, aTextDirective.prefix, true, false);
|
||||
// 2.2.2. If prefixMatch is null, return null.
|
||||
if (!prefixMatch) {
|
||||
DBG("Did not find prefix '%s'. The text directive does not exist "
|
||||
"in the document.",
|
||||
NS_ConvertUTF16toUTF8(aTextDirective.prefix).Data());
|
||||
return nullptr;
|
||||
}
|
||||
DBG("Did find prefix '%s'.",
|
||||
NS_ConvertUTF16toUTF8(aTextDirective.prefix).Data());
|
||||
|
||||
// 2.2.3. Set searchRange’s start to the first boundary point after
|
||||
// prefixMatch’s start
|
||||
const RangeBoundary boundaryPoint = MoveRangeBoundaryOneWord(
|
||||
@@ -521,21 +462,14 @@ RefPtr<nsRange> FragmentDirective::FindRangeForTextDirective(
|
||||
false, mustEndAtWordBoundary);
|
||||
// 2.2.10. If potentialMatch is null, return null.
|
||||
if (!potentialMatch) {
|
||||
DBG("Did not find start '%s'. The text directive does not exist "
|
||||
"in the document.",
|
||||
NS_ConvertUTF16toUTF8(aTextDirective.start).Data());
|
||||
return nullptr;
|
||||
}
|
||||
DBG("Did find start '%s'.",
|
||||
NS_ConvertUTF16toUTF8(aTextDirective.start).Data());
|
||||
// 2.2.11. If potentialMatch’s start is not matchRange’s start, then
|
||||
// continue.
|
||||
// (In this case, we found a prefix but it was followed by something other
|
||||
// than a matching text so we’ll continue searching for the next instance
|
||||
// of prefix.)
|
||||
if (potentialMatch->StartRef() != matchRange->StartRef()) {
|
||||
DBG("The prefix is not directly followed by the start element. "
|
||||
"Discarding this attempt.");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -552,9 +486,6 @@ RefPtr<nsRange> FragmentDirective::FindRangeForTextDirective(
|
||||
true, mustEndAtWordBoundary);
|
||||
// 2.3.3. If potentialMatch is null, return null.
|
||||
if (!potentialMatch) {
|
||||
DBG("Did not find start '%s'. The text directive does not exist "
|
||||
"in the document.",
|
||||
NS_ConvertUTF16toUTF8(aTextDirective.start).Data());
|
||||
return nullptr;
|
||||
}
|
||||
// 2.3.4. Set searchRange’s start to the first boundary point after
|
||||
@@ -594,9 +525,6 @@ RefPtr<nsRange> FragmentDirective::FindRangeForTextDirective(
|
||||
mustEndAtWordBoundary);
|
||||
// 2.5.1.3. If endMatch is null then return null.
|
||||
if (!endMatch) {
|
||||
DBG("Did not find end '%s'. The text directive does not exist "
|
||||
"in the document.",
|
||||
NS_ConvertUTF16toUTF8(aTextDirective.end).Data());
|
||||
return nullptr;
|
||||
}
|
||||
// 2.5.1.4. Set potentialMatch’s end to endMatch’s end.
|
||||
@@ -609,7 +537,6 @@ RefPtr<nsRange> FragmentDirective::FindRangeForTextDirective(
|
||||
|
||||
// 2.5.3. If parsedValues’s suffix is null, return potentialMatch.
|
||||
if (aTextDirective.suffix.IsEmpty()) {
|
||||
DBG("Did find a match.");
|
||||
return potentialMatch;
|
||||
}
|
||||
// 2.5.4. Let suffixRange be a range with start equal to potentialMatch’s
|
||||
@@ -633,9 +560,6 @@ RefPtr<nsRange> FragmentDirective::FindRangeForTextDirective(
|
||||
// (If the suffix doesn't appear in the remaining text of the document,
|
||||
// there's no possible way to make a match.)
|
||||
if (!suffixMatch) {
|
||||
DBG("Did not find suffix '%s'. The text directive does not exist "
|
||||
"in the document.",
|
||||
NS_ConvertUTF16toUTF8(aTextDirective.suffix).Data());
|
||||
return nullptr;
|
||||
}
|
||||
// 2.5.8. If suffixMatch's start is suffixRange's start, return
|
||||
@@ -643,7 +567,6 @@ RefPtr<nsRange> FragmentDirective::FindRangeForTextDirective(
|
||||
if (suffixMatch->GetStartContainer() ==
|
||||
suffixRange->GetStartContainer() &&
|
||||
suffixMatch->StartOffset() == suffixRange->StartOffset()) {
|
||||
DBG("Did find a match.");
|
||||
return potentialMatch;
|
||||
}
|
||||
// 2.5.9. If parsedValue's end item is null then break;
|
||||
@@ -669,29 +592,11 @@ RefPtr<nsRange> FragmentDirective::FindRangeForTextDirective(
|
||||
// matches in step 9 of the above loop. If we couldn’t find a valid
|
||||
// rangeEnd+suffix pair anywhere in the doc then there’s no possible way
|
||||
// to make a match.)
|
||||
// ----
|
||||
// XXX(:jjaschke): Not too sure about this. If a text directive is only
|
||||
// defined by a (prefix +) start element, and the start element happens to
|
||||
// be at the end of the document, `rangeEndSearchRange` could be
|
||||
// collapsed. Therefore, the loop in section 2.5 does not run. Also,
|
||||
// if there would be either an `end` and/or a `suffix`, this would assert
|
||||
// instead of returning `nullptr`, indicating that there's no match.
|
||||
// Instead, the following would make the algorithm more safe:
|
||||
// if there is no end or suffix, the potential match is actually a match,
|
||||
// so return it. Otherwise, the text directive can't be in the document,
|
||||
// therefore return nullptr.
|
||||
if (aTextDirective.end.IsEmpty() && aTextDirective.suffix.IsEmpty()) {
|
||||
DBG("rangeEndSearchRange was collapsed, no end or suffix "
|
||||
"present. Returning a match");
|
||||
return potentialMatch;
|
||||
}
|
||||
DBG("rangeEndSearchRange was collapsed, there is an end or "
|
||||
"suffix. There can't be a match.");
|
||||
return nullptr;
|
||||
// XXX(:jjaschke): should this really assert?
|
||||
MOZ_ASSERT(!aTextDirective.end.IsEmpty());
|
||||
}
|
||||
}
|
||||
// 3. Return null.
|
||||
DBG("Did not find a match.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -857,29 +762,111 @@ RefPtr<nsRange> FragmentDirective::FindStringInRange(nsRange* aSearchRange,
|
||||
bool aWordStartBounded,
|
||||
bool aWordEndBounded) {
|
||||
MOZ_ASSERT(aSearchRange);
|
||||
DBG("query='%s', wordStartBounded='%d', wordEndBounded='%d'.\n",
|
||||
NS_ConvertUTF16toUTF8(aQuery).Data(), aWordStartBounded, aWordEndBounded);
|
||||
RefPtr<nsFind> finder = new nsFind();
|
||||
finder->SetWordStartBounded(aWordStartBounded);
|
||||
finder->SetWordEndBounded(aWordEndBounded);
|
||||
finder->SetCaseSensitive(false);
|
||||
RefPtr<nsRange> searchRangeStart = nsRange::Create(
|
||||
aSearchRange->StartRef(), aSearchRange->StartRef(), IgnoreErrors());
|
||||
RefPtr<nsRange> searchRangeEnd = nsRange::Create(
|
||||
aSearchRange->EndRef(), aSearchRange->EndRef(), IgnoreErrors());
|
||||
RefPtr<nsRange> result;
|
||||
Unused << finder->Find(aQuery, aSearchRange, searchRangeStart, searchRangeEnd,
|
||||
getter_AddRefs(result));
|
||||
if (!result || result->Collapsed()) {
|
||||
DBG("Did not find query '%s'", NS_ConvertUTF16toUTF8(aQuery).Data());
|
||||
} else {
|
||||
auto rangeToString = [](nsRange* range) -> nsCString {
|
||||
nsString rangeString;
|
||||
range->ToString(rangeString, IgnoreErrors());
|
||||
return NS_ConvertUTF16toUTF8(rangeString);
|
||||
};
|
||||
DBG("find returned '%s'", rangeToString(result).Data());
|
||||
RefPtr<nsRange> searchRange = aSearchRange->CloneRange();
|
||||
// 1. While searchRange is not collapsed
|
||||
while (searchRange && !searchRange->Collapsed()) {
|
||||
// 1.1. Let curNode be searchRange’s start node.
|
||||
RefPtr<nsINode> curNode = searchRange->GetStartContainer();
|
||||
|
||||
// 1.2. If curNode is part of a non-searchable subtree:
|
||||
if (NodeIsPartOfNonSearchableSubTree(*curNode)) {
|
||||
// 1.2.1. Set searchRange’s start node to the next node, in
|
||||
// shadow-including tree order, that isn’t a shadow-including descendant
|
||||
// of curNode.
|
||||
RefPtr<nsINode> next = curNode;
|
||||
while ((next = next->GetNextNode())) {
|
||||
if (!next->IsShadowIncludingInclusiveDescendantOf(curNode)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!next) {
|
||||
return nullptr;
|
||||
}
|
||||
// 1.2.2. Set `searchRange`s `start offset` to 0
|
||||
searchRange->SetStart(next, 0);
|
||||
// 1.2.3. continue.
|
||||
continue;
|
||||
}
|
||||
// 1.3. If curNode is not a visible TextNode:
|
||||
if (!NodeIsVisibleTextNode(*curNode)) {
|
||||
// 1.3.1. Set searchRange’s start node to the next node, in
|
||||
// shadow-including tree order, that is not a doctype.
|
||||
RefPtr<nsINode> next = curNode;
|
||||
while ((next = next->GetNextNode())) {
|
||||
if (next->NodeType() != Node_Binding::DOCUMENT_TYPE_NODE) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!next) {
|
||||
return nullptr;
|
||||
}
|
||||
// 1.3.2. Set searchRange’s start offset to 0.
|
||||
searchRange->SetStart(next, 0);
|
||||
// 1.3.3. continue.
|
||||
continue;
|
||||
}
|
||||
// 1.4. Let blockAncestor be the nearest block ancestor of `curNode`
|
||||
RefPtr<nsINode> blockAncestor = GetBlockAncestorForNode(curNode);
|
||||
|
||||
// 1.5. Let textNodeList be a list of Text nodes, initially empty.
|
||||
nsTArray<RefPtr<Text>> textNodeList;
|
||||
// 1.6. While curNode is a shadow-including descendant of blockAncestor and
|
||||
// the position of the boundary point (curNode,0) is not after searchRange's
|
||||
// end:
|
||||
while (curNode &&
|
||||
curNode->IsShadowIncludingInclusiveDescendantOf(blockAncestor)) {
|
||||
Maybe<int32_t> comp = nsContentUtils::ComparePoints(
|
||||
curNode, 0, searchRange->GetEndContainer(), searchRange->EndOffset());
|
||||
if (comp) {
|
||||
if (*comp >= 0) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// This means that the compared nodes are disconnected.
|
||||
return nullptr;
|
||||
}
|
||||
// 1.6.1. If curNode has block-level display, then break.
|
||||
if (NodeHasBlockLevelDisplay(*curNode)) {
|
||||
break;
|
||||
}
|
||||
// 1.6.2. If curNode is search invisible:
|
||||
if (NodeIsSearchInvisible(*curNode)) {
|
||||
// 1.6.2.1. Set curNode to the next node, in shadow-including tree
|
||||
// order, that isn't a shadow-including descendant of curNode.
|
||||
curNode = curNode->GetNextNode();
|
||||
// 1.6.2.2. Continue.
|
||||
continue;
|
||||
}
|
||||
// 1.6.3. If curNode is a visible text node then append it to
|
||||
// textNodeList.
|
||||
if (NodeIsVisibleTextNode(*curNode)) {
|
||||
textNodeList.AppendElement(curNode->AsText());
|
||||
}
|
||||
// 1.6.4. Set curNode to the next node in shadow-including
|
||||
// tree order.
|
||||
curNode = curNode->GetNextNode();
|
||||
}
|
||||
// 1.7. Run the find a range from a node list steps given
|
||||
// query, searchRange, textNodeList, wordStartBounded, wordEndBounded as
|
||||
// input. If the resulting Range is not null, then return it.
|
||||
if (RefPtr<nsRange> range =
|
||||
FindRangeFromNodeList(searchRange, aQuery, textNodeList,
|
||||
aWordStartBounded, aWordEndBounded)) {
|
||||
return range;
|
||||
}
|
||||
|
||||
// 1.8. If curNode is null, then break.
|
||||
if (!curNode) {
|
||||
break;
|
||||
}
|
||||
|
||||
// 1.9. Assert: curNode follows searchRange's start node.
|
||||
|
||||
// 1.10. Set searchRange's start to the boundary point (curNode,0).
|
||||
searchRange->SetStart(curNode, 0);
|
||||
}
|
||||
return result;
|
||||
|
||||
// 2. Return null.
|
||||
return nullptr;
|
||||
}
|
||||
} // namespace mozilla::dom
|
||||
|
||||
@@ -75,11 +75,6 @@ class FragmentDirective final : public nsISupports, public nsWrapperCache {
|
||||
/** Clears all uninvoked directives. */
|
||||
void ClearUninvokedDirectives() { mUninvokedTextDirectives.Clear(); }
|
||||
|
||||
/** Inserts all text directive ranges into a `eTargetText` `Selection`. */
|
||||
MOZ_CAN_RUN_SCRIPT
|
||||
void HighlightTextDirectives(
|
||||
const nsTArray<RefPtr<nsRange>>& aTextDirectiveRanges);
|
||||
|
||||
/** Searches for the current uninvoked text directives and creates a range for
|
||||
* each one that is found.
|
||||
*
|
||||
|
||||
@@ -3062,8 +3062,7 @@ UniquePtr<gfxContext> PresShell::CreateReferenceRenderingContext() {
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/#scroll-to-the-fragment-identifier
|
||||
nsresult PresShell::GoToAnchor(const nsAString& aAnchorName,
|
||||
const nsRange* aFirstTextDirective, bool aScroll,
|
||||
nsresult PresShell::GoToAnchor(const nsAString& aAnchorName, bool aScroll,
|
||||
ScrollFlags aAdditionalScrollFlags) {
|
||||
if (!mDocument) {
|
||||
return NS_ERROR_FAILURE;
|
||||
@@ -3082,31 +3081,12 @@ nsresult PresShell::GoToAnchor(const nsAString& aAnchorName,
|
||||
// Hold a reference to the ESM in case event dispatch tears us down.
|
||||
RefPtr<EventStateManager> esm = mPresContext->EventStateManager();
|
||||
|
||||
// https://wicg.github.io/scroll-to-text-fragment/#invoking-text-directives
|
||||
// From "Monkeypatching HTML § 7.4.6.3 Scrolling to a fragment:"
|
||||
// 3.4. If target is a range, then:
|
||||
// 3.4.1 Set target to be the first common ancestor of target's start node and
|
||||
// end node.
|
||||
// 3.4.2 While target is non-null and is not an element, set target to
|
||||
// target's parent.
|
||||
Element* textFragmentTargetElement = [&aFirstTextDirective]() -> Element* {
|
||||
nsINode* node =
|
||||
aFirstTextDirective
|
||||
? aFirstTextDirective->GetClosestCommonInclusiveAncestor()
|
||||
: nullptr;
|
||||
while (node && !node->IsElement()) {
|
||||
node = node->GetParent();
|
||||
}
|
||||
return Element::FromNodeOrNull(node);
|
||||
}();
|
||||
const bool thereIsATextFragment = !!textFragmentTargetElement;
|
||||
|
||||
// 1. If there is no indicated part of the document, set the Document's target
|
||||
// element to null.
|
||||
//
|
||||
// FIXME(emilio): Per spec empty fragment string should take the same
|
||||
// code-path as "top"!
|
||||
if (aAnchorName.IsEmpty() && !thereIsATextFragment) {
|
||||
if (aAnchorName.IsEmpty()) {
|
||||
NS_ASSERTION(!aScroll, "can't scroll to empty anchor name");
|
||||
esm->SetContentState(nullptr, ElementState::URLTARGET);
|
||||
return NS_OK;
|
||||
@@ -3120,10 +3100,8 @@ nsresult PresShell::GoToAnchor(const nsAString& aAnchorName,
|
||||
//
|
||||
// https://html.spec.whatwg.org/#target-element
|
||||
// https://html.spec.whatwg.org/#find-a-potential-indicated-element
|
||||
RefPtr<Element> target = textFragmentTargetElement;
|
||||
if (!target) {
|
||||
target = nsContentUtils::GetTargetElement(mDocument, aAnchorName);
|
||||
}
|
||||
RefPtr<Element> target =
|
||||
nsContentUtils::GetTargetElement(mDocument, aAnchorName);
|
||||
|
||||
// 1. If there is no indicated part of the document, set the Document's
|
||||
// target element to null.
|
||||
@@ -3142,12 +3120,6 @@ nsresult PresShell::GoToAnchor(const nsAString& aAnchorName,
|
||||
|
||||
if (target) {
|
||||
if (aScroll) {
|
||||
// https://wicg.github.io/scroll-to-text-fragment/#invoking-text-directives
|
||||
// From "Monkeypatching HTML § 7.4.6.3 Scrolling to a fragment:"
|
||||
// 3.9 Let blockPosition be "center" if scrollTarget is a range, "start"
|
||||
// otherwise.
|
||||
const auto verticalScrollPosition = WhereToScroll(
|
||||
thereIsATextFragment ? WhereToScroll::Center : WhereToScroll::Start);
|
||||
// 3.3. TODO: Run the ancestor details revealing algorithm on target.
|
||||
// 3.4. Scroll target into view, with behavior set to "auto", block set to
|
||||
// "start", and inline set to "nearest".
|
||||
@@ -3155,7 +3127,7 @@ nsresult PresShell::GoToAnchor(const nsAString& aAnchorName,
|
||||
// smooth scroll for `top` regardless below, so maybe they should!).
|
||||
ScrollingInteractionContext scrollToAnchorContext(true);
|
||||
MOZ_TRY(ScrollContentIntoView(
|
||||
target, ScrollAxis(verticalScrollPosition, WhenToScroll::Always),
|
||||
target, ScrollAxis(WhereToScroll::Start, WhenToScroll::Always),
|
||||
ScrollAxis(),
|
||||
ScrollFlags::AnchorScrollFlags | aAdditionalScrollFlags));
|
||||
|
||||
@@ -3273,6 +3245,46 @@ nsresult PresShell::ScrollToAnchor() {
|
||||
ScrollAxis(), ScrollFlags::AnchorScrollFlags);
|
||||
}
|
||||
|
||||
bool PresShell::HighlightAndGoToTextFragment(bool aScrollToTextFragment) {
|
||||
MOZ_ASSERT(mDocument);
|
||||
if (!StaticPrefs::dom_text_fragments_enabled()) {
|
||||
return false;
|
||||
}
|
||||
const RefPtr<FragmentDirective> fragmentDirective =
|
||||
mDocument->FragmentDirective();
|
||||
|
||||
nsTArray<RefPtr<nsRange>> textDirectiveRanges =
|
||||
fragmentDirective->FindTextFragmentsInDocument();
|
||||
if (textDirectiveRanges.IsEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const RefPtr<Selection> targetTextSelection =
|
||||
GetCurrentSelection(SelectionType::eTargetText);
|
||||
if (!targetTextSelection) {
|
||||
return false;
|
||||
}
|
||||
for (RefPtr<nsRange> range : textDirectiveRanges) {
|
||||
targetTextSelection->AddRangeAndSelectFramesAndNotifyListeners(
|
||||
*range, IgnoreErrors());
|
||||
}
|
||||
if (!aScrollToTextFragment) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Scroll the last text directive into view.
|
||||
nsRange* lastRange = textDirectiveRanges.LastElement();
|
||||
MOZ_ASSERT(lastRange);
|
||||
if (RefPtr<nsIContent> lastRangeStartContent =
|
||||
nsIContent::FromNode(lastRange->GetStartContainer())) {
|
||||
return ScrollContentIntoView(
|
||||
lastRangeStartContent,
|
||||
ScrollAxis(WhereToScroll::Center, WhenToScroll::Always),
|
||||
ScrollAxis(), ScrollFlags::AnchorScrollFlags) == NS_OK;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper (per-continuation) for ScrollContentIntoView.
|
||||
*
|
||||
|
||||
@@ -1563,7 +1563,7 @@ class PresShell final : public nsStubDocumentObserver,
|
||||
|
||||
/**
|
||||
* Informs the pres shell that the document is now at the anchor with
|
||||
* the given name or range. If |aScroll| is true, scrolls the view of the
|
||||
* the given name. If |aScroll| is true, scrolls the view of the
|
||||
* document so that the anchor with the specified name is displayed at
|
||||
* the top of the window. If |aAnchorName| is empty, then this informs
|
||||
* the pres shell that there is no current target, and |aScroll| must
|
||||
@@ -1571,8 +1571,7 @@ class PresShell final : public nsStubDocumentObserver,
|
||||
* and |aScroll| is true, the scrolling may be performed with an animation.
|
||||
*/
|
||||
MOZ_CAN_RUN_SCRIPT
|
||||
nsresult GoToAnchor(const nsAString& aAnchorName,
|
||||
const nsRange* aFirstTextDirective, bool aScroll,
|
||||
nsresult GoToAnchor(const nsAString& aAnchorName, bool aScroll,
|
||||
ScrollFlags aAdditionalScrollFlags = ScrollFlags::None);
|
||||
|
||||
/**
|
||||
@@ -1585,6 +1584,18 @@ class PresShell final : public nsStubDocumentObserver,
|
||||
*/
|
||||
MOZ_CAN_RUN_SCRIPT nsresult ScrollToAnchor();
|
||||
|
||||
/**
|
||||
* Finds text fragments ranes in the document, highlights the ranges and
|
||||
* scrolls to the last text fragment range on the page if
|
||||
* `aScrollToTextFragment` is true.
|
||||
*
|
||||
* @param aScrollToTextFragment If true, scrolls the view to the last text
|
||||
* fragment.
|
||||
* @return True if scrolling happened.
|
||||
*/
|
||||
MOZ_CAN_RUN_SCRIPT bool HighlightAndGoToTextFragment(
|
||||
bool aScrollToTextFragment);
|
||||
|
||||
/**
|
||||
* When scroll anchoring adjusts positions in the root frame during page load,
|
||||
* it may move our scroll position in the root frame.
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
#include "mozilla/dom/Document.h"
|
||||
#include "mozilla/dom/DocumentInlines.h"
|
||||
#include "mozilla/dom/DocGroup.h"
|
||||
#include "mozilla/dom/FragmentDirective.h"
|
||||
#include "mozilla/widget/Screen.h"
|
||||
#include "nsPresContext.h"
|
||||
#include "nsIFrame.h"
|
||||
@@ -1078,10 +1077,6 @@ nsDocumentViewer::LoadComplete(nsresult aStatus) {
|
||||
|
||||
if (!mStopped) {
|
||||
if (mDocument) {
|
||||
// This is the final attempt to scroll to an anchor / text directive.
|
||||
// This is the last iteration of the algorithm described in the spec for
|
||||
// trying to scroll to a fragment.
|
||||
// https://html.spec.whatwg.org/#try-to-scroll-to-the-fragment
|
||||
nsCOMPtr<Document> document = mDocument;
|
||||
document->ScrollToRef();
|
||||
}
|
||||
@@ -1098,19 +1093,6 @@ nsDocumentViewer::LoadComplete(nsresult aStatus) {
|
||||
}
|
||||
}
|
||||
|
||||
// https://wicg.github.io/scroll-to-text-fragment/#invoking-text-directives
|
||||
// Monkeypatching HTML § 7.4.6.3 Scrolling to a fragment:
|
||||
// 2.1 If the user agent has reason to believe the user is no longer
|
||||
// interested in scrolling to the fragment, then:
|
||||
// 2.1.1 Set pending text directives to null.
|
||||
//
|
||||
// Gecko's implementation differs from the spec (ie., it implements its
|
||||
// intention but doesn't follow step by step), therefore the mentioned steps
|
||||
// are not applied in the same manner.
|
||||
// However, this should be the right place to do this.
|
||||
if (mDocument) {
|
||||
mDocument->FragmentDirective()->ClearUninvokedDirectives();
|
||||
}
|
||||
if (mDocument && !restoring) {
|
||||
mDocument->LoadEventFired();
|
||||
}
|
||||
|
||||
@@ -3,7 +3,17 @@
|
||||
expected: FAIL
|
||||
|
||||
[Cross-origin with element-id fallback]
|
||||
expected: [FAIL, PASS]
|
||||
expected: FAIL
|
||||
|
||||
[Navigate cross-origin iframe via window.location]
|
||||
expected: [FAIL, PASS]
|
||||
expected:
|
||||
if (os == "mac") and not debug: [PASS, FAIL]
|
||||
FAIL
|
||||
|
||||
[Navigate same-origin iframe via window.location]
|
||||
expected:
|
||||
if (os == "mac") and not debug: [PASS, FAIL]
|
||||
|
||||
[Non-matching text with element-id fallback]
|
||||
expected:
|
||||
if (os == "mac") and not debug: [PASS, FAIL]
|
||||
|
||||
@@ -12,6 +12,3 @@
|
||||
|
||||
[Text directive blocked in application/xml]
|
||||
expected: [NOTRUN, FAIL, TIMEOUT, PASS]
|
||||
|
||||
[Text directive blocked in text/css]
|
||||
expected: FAIL
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
[percent-encoding.html]
|
||||
[Test navigation with fragment: Percent char without hex digits is invalid..]
|
||||
expected:
|
||||
if (os == "mac") and not debug: [PASS, FAIL]
|
||||
|
||||
[Test navigation with fragment: Percent char followed by percent char is invalid..]
|
||||
expected:
|
||||
if (os == "mac") and not debug: [PASS, FAIL]
|
||||
|
||||
[Test navigation with fragment: Single digit percent-encoding is invalid..]
|
||||
expected:
|
||||
if (os == "mac") and not debug: [PASS, FAIL]
|
||||
|
||||
[Test navigation with fragment: Percent-encoding limited to two digits..]
|
||||
expected:
|
||||
if (os == "mac") and not debug: [PASS, FAIL]
|
||||
|
||||
[Test navigation with fragment: Percent-encoded "%%F".]
|
||||
expected: FAIL
|
||||
|
||||
[Test navigation with fragment: Percent-encoding multibyte codepoint (CHECKMARK)..]
|
||||
expected:
|
||||
if (os == "mac") and not debug: [PASS, FAIL]
|
||||
@@ -1,4 +1,92 @@
|
||||
[scroll-to-text-fragment.html]
|
||||
[Test navigation with fragment: Text directive should horizontally scroll into view.]
|
||||
[Test navigation with fragment: Text directive with invalid syntax (context terms without "-") should not parse as a text directive.]
|
||||
expected:
|
||||
if (os == "android"): [PASS, FAIL]
|
||||
if (os == "mac") and not debug: [PASS, FAIL]
|
||||
|
||||
[Test navigation with fragment: Exact text with no context should match text.]
|
||||
expected: FAIL
|
||||
|
||||
[Test navigation with fragment: Exact text with prefix should match text.]
|
||||
expected: FAIL
|
||||
|
||||
[Test navigation with fragment: Exact text with suffix should match text.]
|
||||
expected: FAIL
|
||||
|
||||
[Test navigation with fragment: Exact text with prefix and suffix should match text.]
|
||||
expected: FAIL
|
||||
|
||||
[Test navigation with fragment: Exact text with prefix and suffix and query equals prefix..]
|
||||
expected: FAIL
|
||||
|
||||
[Test navigation with fragment: Text range with no context should match text.]
|
||||
expected: FAIL
|
||||
|
||||
[Test navigation with fragment: Text range with prefix should match text.]
|
||||
expected: FAIL
|
||||
|
||||
[Test navigation with fragment: Text range with suffix should match text.]
|
||||
expected: FAIL
|
||||
|
||||
[Test navigation with fragment: Text range with prefix and suffix should match text.]
|
||||
expected: FAIL
|
||||
|
||||
[Test navigation with fragment: Exact text with percent encoded spaces should match text.]
|
||||
expected: FAIL
|
||||
|
||||
[Test navigation with fragment: Fragment directive with percent encoded syntactical characters "&,-" should match text.]
|
||||
expected: FAIL
|
||||
|
||||
[Test navigation with fragment: Fragment directive with percent encoded non-ASCII unicode character should match text.]
|
||||
expected: FAIL
|
||||
|
||||
[Test navigation with fragment: Fragment directive with all TextMatchChars should match text.]
|
||||
expected: FAIL
|
||||
|
||||
[Test navigation with fragment: Multiple matching exact texts should match text.]
|
||||
expected: FAIL
|
||||
|
||||
[Test navigation with fragment: A non-matching text directive followed by a matching text directive should match and scroll into view the second text directive.]
|
||||
expected: FAIL
|
||||
|
||||
[Test navigation with fragment: Text directive followed by non-text directive should match text.]
|
||||
expected: FAIL
|
||||
|
||||
[Test navigation with fragment: Multiple text directives and a non-text directive should match text.]
|
||||
expected: FAIL
|
||||
|
||||
[Test navigation with fragment: Text directive with existing element fragment should match and scroll into view text.]
|
||||
expected: FAIL
|
||||
|
||||
[Test navigation with fragment: Text directive with nonexistent element fragment should match and scroll into view text.]
|
||||
expected: FAIL
|
||||
|
||||
[Test navigation with fragment: Multiple match text directive disambiguated by prefix should match the prefixed text.]
|
||||
expected: FAIL
|
||||
|
||||
[Test navigation with fragment: Multiple match text directive disambiguated by suffix should match the suffixed text.]
|
||||
expected: FAIL
|
||||
|
||||
[Test navigation with fragment: Multiple match text directive disambiguated by prefix and suffix should match the text with the given context.]
|
||||
expected: FAIL
|
||||
|
||||
[Test navigation with fragment: Text directive should match when context terms are separated by node boundaries.]
|
||||
expected: FAIL
|
||||
|
||||
[Test navigation with fragment: Text directive should match text within shadow DOM.]
|
||||
expected: FAIL
|
||||
|
||||
[Test navigation with fragment: Text directive should horizontally scroll into view.]
|
||||
expected: FAIL
|
||||
|
||||
[Test navigation with fragment: Uppercase TEXT directive should not parse as a text directive.]
|
||||
expected:
|
||||
if (os == "mac") and not debug: [PASS, FAIL]
|
||||
|
||||
[Test navigation with fragment: Generic fragment directive with existing element fragment should scroll to element.]
|
||||
expected:
|
||||
if (os == "mac") and not debug: [PASS, FAIL]
|
||||
if (os == "android") and debug: [PASS, FAIL]
|
||||
|
||||
[Test navigation with fragment: Non-matching text directive with existing element fragment should scroll to element.]
|
||||
expected:
|
||||
if (os == "android") and debug: [PASS, FAIL]
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<title>
|
||||
Navigating to a text fragment which is only available after `DOMContentLoaded`
|
||||
</title>
|
||||
<script src="stash.js"></script>
|
||||
<script>
|
||||
function checkScroll() {
|
||||
const results = {hasScrolled: window.scrollY != 0};
|
||||
let key = (new URL(document.location)).searchParams.get("key");
|
||||
stashResultsThenClose(key, results);
|
||||
};
|
||||
window.onload = () => {
|
||||
window.requestAnimationFrame(function() {
|
||||
window.requestAnimationFrame(checkScroll);
|
||||
})
|
||||
}
|
||||
</script>
|
||||
<body>
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
document.body.innerHTML = `<div style="margin-top: 200vh">After DOMContentLoaded</div>`;
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
@@ -1,25 +0,0 @@
|
||||
<!doctype html>
|
||||
<title>Navigating to a text fragment directive</title>
|
||||
<meta charset=utf-8>
|
||||
<link rel="help" href="https://wicg.github.io/ScrollToTextFragment/">
|
||||
<meta name="timeout" content="long">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/resources/testdriver.js"></script>
|
||||
<script src="/resources/testdriver-vendor.js"></script>
|
||||
<script src="/common/utils.js"></script>
|
||||
<script src="stash.js"></script>
|
||||
|
||||
<script>
|
||||
promise_test(t => new Promise((resolve, reject) => {
|
||||
let key = token();
|
||||
test_driver.bless('Open a URL with a text fragment directive', () => {
|
||||
window.open(`scroll-to-text-fragment-after-DOMContentLoaded-target.html?key=${key}#:~:text=DOMContentLoaded`, "_blank", "noopener");
|
||||
|
||||
fetchResults(key, resolve, reject);
|
||||
})
|
||||
}).then(data => {
|
||||
assert_true(data.hasScrolled, "Expected text directive to be scrolled to.");
|
||||
})
|
||||
);
|
||||
</script>
|
||||
@@ -46,11 +46,6 @@ let test_cases = [
|
||||
expect_position: 'text',
|
||||
description: 'Exact text with no context should match text'
|
||||
},
|
||||
{
|
||||
fragment: '#:~:text=TEST',
|
||||
expect_position: 'text',
|
||||
description: 'Case-insensitive search with no context should match text'
|
||||
},
|
||||
{
|
||||
fragment: '#:~:text=this is a-,test',
|
||||
expect_position: 'text',
|
||||
|
||||
@@ -13,10 +13,6 @@ XPIDL_SOURCES += [
|
||||
"nsIWebBrowserFind.idl",
|
||||
]
|
||||
|
||||
EXPORTS += [
|
||||
"nsFind.h",
|
||||
]
|
||||
|
||||
XPIDL_MODULE = "mozfind"
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
|
||||
@@ -480,13 +480,13 @@ NS_IMETHODIMP
|
||||
nsFind::GetEntireWord(bool* aEntireWord) {
|
||||
if (!aEntireWord) return NS_ERROR_NULL_POINTER;
|
||||
|
||||
*aEntireWord = mWordStartBounded && mWordEndBounded;
|
||||
*aEntireWord = mEntireWord;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsFind::SetEntireWord(bool aEntireWord) {
|
||||
mWordStartBounded = mWordEndBounded = aEntireWord;
|
||||
mEntireWord = aEntireWord;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@@ -861,7 +861,7 @@ nsFind::Find(const nsAString& aPatText, nsRange* aSearchRange,
|
||||
// Figure whether the previous char is a word-breaking one,
|
||||
// if we care about word boundaries.
|
||||
bool wordBreakPrev = true;
|
||||
if (mWordStartBounded && prevChar) {
|
||||
if (mEntireWord && prevChar) {
|
||||
if (prevChar == NBSP_CHARCODE) {
|
||||
prevChar = CHAR_TO_UNICHAR(' ');
|
||||
}
|
||||
@@ -874,8 +874,7 @@ nsFind::Find(const nsAString& aPatText, nsRange* aSearchRange,
|
||||
// b) a match has already been stored
|
||||
// c) the previous character is a different "class" than the current
|
||||
// character.
|
||||
if ((c == patc && (!(mWordStartBounded || mWordEndBounded) ||
|
||||
matchAnchorNode || wordBreakPrev)) ||
|
||||
if ((c == patc && (!mEntireWord || matchAnchorNode || wordBreakPrev)) ||
|
||||
(inWhitespace && IsSpace(c))) {
|
||||
prevCharInMatch = c;
|
||||
if (inWhitespace) {
|
||||
@@ -902,7 +901,7 @@ nsFind::Find(const nsAString& aPatText, nsRange* aSearchRange,
|
||||
|
||||
// Make the range:
|
||||
// Check for word break (if necessary)
|
||||
if (mWordEndBounded || inWhitespace) {
|
||||
if (mEntireWord || inWhitespace) {
|
||||
int32_t nextfindex = findex + incr;
|
||||
|
||||
char32_t nextChar;
|
||||
@@ -923,7 +922,7 @@ nsFind::Find(const nsAString& aPatText, nsRange* aSearchRange,
|
||||
}
|
||||
|
||||
// If a word break isn't there when it needs to be, reset search.
|
||||
if (mWordEndBounded && nextChar && !BreakInBetween(c, nextChar)) {
|
||||
if (mEntireWord && nextChar && !BreakInBetween(c, nextChar)) {
|
||||
matchAnchorNode = nullptr;
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -28,19 +28,6 @@ class nsFind : public nsIFind {
|
||||
NS_DECL_NSIFIND
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS(nsFind)
|
||||
|
||||
// The IDL interface allows to toggle the "find entire words" search mode
|
||||
// by calling `SetEntireWord()`.
|
||||
// Internally, it is possible to specify word boundaries at the beginning
|
||||
// and/or the end of the search pattern.
|
||||
// `GetEntireWord()` returns true if `mWordStartBounded` and `mWordEndBounded`
|
||||
// are true.
|
||||
void SetWordStartBounded(bool aWordStartBounded) {
|
||||
mWordStartBounded = aWordStartBounded;
|
||||
}
|
||||
void SetWordEndBounded(bool aWordEndBounded) {
|
||||
mWordEndBounded = aWordEndBounded;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual ~nsFind() = default;
|
||||
|
||||
@@ -49,8 +36,9 @@ class nsFind : public nsIFind {
|
||||
bool mCaseSensitive = false;
|
||||
bool mMatchDiacritics = false;
|
||||
|
||||
bool mWordStartBounded = false;
|
||||
bool mWordEndBounded = false;
|
||||
// Use "find entire words" mode by setting mEntireWord to true; or false to
|
||||
// disable "entire words" mode.
|
||||
bool mEntireWord = false;
|
||||
|
||||
struct State;
|
||||
class StateRestorer;
|
||||
|
||||
Reference in New Issue
Block a user