Source code
Revision control
Copy as Markdown
Other Tools
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
#ifndef DOM_TEXTDIRECTIVEUTIL_H_
#define DOM_TEXTDIRECTIVEUTIL_H_
#include "mozilla/Logging.h"
#include "mozilla/RangeBoundary.h"
#include "mozilla/RefPtr.h"
#include "mozilla/TimeStamp.h"
#include "nsStringFwd.h"
class nsIURI;
class nsINode;
class nsRange;
struct TextDirective;
namespace mozilla::dom {
extern LazyLogModule gFragmentDirectiveLog;
#define TEXT_FRAGMENT_LOG_FN(msg, func, ...) \
MOZ_LOG_FMT(gFragmentDirectiveLog, LogLevel::Debug, "{}(): " msg, func, \
##__VA_ARGS__)
// Shortcut macro for logging, which includes the current function name.
// To customize (eg. if in a lambda), use `TEXT_FRAGMENT_LOG_FN`.
#define TEXT_FRAGMENT_LOG(msg, ...) \
TEXT_FRAGMENT_LOG_FN(msg, __FUNCTION__, ##__VA_ARGS__)
enum class TextScanDirection { Left = -1, Right = 1 };
class TextDirectiveUtil final {
public:
MOZ_ALWAYS_INLINE static bool ShouldLog() {
return MOZ_LOG_TEST(gFragmentDirectiveLog, LogLevel::Debug);
}
static Result<nsString, ErrorResult> RangeContentAsString(nsRange* aRange);
static Result<nsString, ErrorResult> RangeContentAsFoldCase(nsRange* aRange);
/**
* @brief Return true if `aNode` is a visible Text node.
*
* A node is a visible text node if it is a Text node, the computed value of
* its parent element's visibility property is visible, and it is being
* rendered.
*
*/
static bool NodeIsVisibleTextNode(const nsINode& aNode);
/**
* @brief Finds the search query in the given search range.
*
* This is a thin wrapper around `nsFind`.
*/
static RefPtr<nsRange> FindStringInRange(
const RangeBoundary& aSearchStart, const RangeBoundary& aSearchEnd,
const nsAString& aQuery, bool aWordStartBounded, bool aWordEndBounded,
nsContentUtils::NodeIndexCache* aCache = nullptr);
/**
* @brief Moves `aRangeBoundary` one word in `aDirection`.
*
* Word boundaries are determined using `intl::WordBreaker::FindWord()`.
*
*
* @param aRangeBoundary[in] The range boundary that should be moved.
* Must be set and valid.
* @param aDirection[in] The direction into which to move.
* @return A new `RangeBoundary` which is moved to the next word.
*/
static RangeBoundary MoveRangeBoundaryOneWord(
const RangeBoundary& aRangeBoundary, TextScanDirection aDirection);
/**
* @brief Tests if there is whitespace at the given position.
*
* This algorithm tests for whitespaces and ` ` at `aPos`.
* It returns true if whitespace was found.
*
* This function assumes the reading direction is "right". If trying to check
* for whitespace to the left, the caller must adjust the offset.
*
*/
static bool IsWhitespaceAtPosition(const Text* aText, uint32_t aPos);
/**
* @brief Determine if `aNode` should be considered when traversing the DOM.
*
* A node is "search invisible" if it is an element in the HTML namespace and
* 1. The computed value of its `display` property is `none`
* 2. It serializes as void
* 3. It is one of the following types:
* - HTMLIFrameElement
* - HTMLImageElement
* - HTMLMeterElement
* - HTMLObjectElement
* - HTMLProgressElement
* - HTMLStyleElement
* - HTMLScriptElement
* - HTMLVideoElement
* - HTMLAudioElement
* 4. It is a `select` element whose `multiple` content attribute is absent
*
*/
static bool NodeIsSearchInvisible(nsINode& aNode);
/**
* @brief Returns true if `aNode` has block-level display.
* A node has block-level display if it is an element and the computed value
* of its display property is any of
* - block
* - table
* - flow-root
* - grid
* - flex
* - list-item
*
*/
static bool NodeHasBlockLevelDisplay(nsINode& aNode);
/**
* @brief Get the Block Ancestor For `aNode`.
*
*/
static nsINode* GetBlockAncestorForNode(nsINode* aNode);
/**
* @brief Returns true if `aNode` is part of a non-searchable subtree.
*
* A node is part of a non-searchable subtree if it is or has a
* shadow-including ancestor that is search invisible.
*
*/
static bool NodeIsPartOfNonSearchableSubTree(nsINode& aNode);
/**
* @brief Convenience function that returns true if the given position in a
* string is a word boundary.
*
* This is a thin wrapper around the `WordBreaker::FindWord()` function.
*
* @param aText The text input.
* @param aPosition The position to check.
* @return true if there is a word boundary at `aPosition`.
* @return false otherwise.
*/
static bool IsAtWordBoundary(const nsAString& aText, uint32_t aPosition);
enum class IsEndIndex : bool { No, Yes };
static RangeBoundary GetBoundaryPointAtIndex(
uint32_t aIndex, const nsTArray<RefPtr<Text>>& aTextNodeList,
IsEndIndex aIsEndIndex);
/** Advances the start of `aRange` to the next non-whitespace position.
* The function follows this section of the spec:
*/
static void AdvanceStartToNextNonWhitespacePosition(nsRange& aRange);
static RangeBoundary MoveBoundaryToNextNonWhitespacePosition(
const RangeBoundary& aRangeBoundary);
static RangeBoundary MoveBoundaryToPreviousNonWhitespacePosition(
const RangeBoundary& aRangeBoundary);
static Result<Maybe<RangeBoundary>, ErrorResult> FindBlockBoundaryInRange(
const nsRange& aRange, TextScanDirection aDirection);
static Result<RangeBoundary, ErrorResult> FindNextBlockBoundary(
const RangeBoundary& aRangeBoundary, TextScanDirection aDirection);
/**
* @brief Compares two range boundaries whether they are "normalized equal".
*
* Range boundaries are "normalized equal" if there is no visible text between
* them, for example here (range boundaries represented by `|`):
*
* ```html
* <span>foo |<p>|bar</p></span>
* ```
*
* In this case, comparing the boundaries for equality would return false.
* But, when calling this function, they would be considered normalized equal.
*
* @return true if the boundaries are normalized equal.
*/
static bool NormalizedRangeBoundariesAreEqual(
const RangeBoundary& aRangeBoundary1,
const RangeBoundary& aRangeBoundary2,
nsContentUtils::NodeIndexCache* aCache = nullptr);
/**
* @brief Extends the range boundaries to word boundaries across nodes.
*
* @param[inout] aRange The range. Changes to the range are done in-place.
*
* @return Returns an error value if something failed along the way.
*/
static Result<Ok, ErrorResult> ExtendRangeToWordBoundaries(nsRange& aRange);
/**
* @brief Create a `TextDirective` From `nsRange`s representing the context
* terms.
*
* Every parameter besides `aStart` is allowed to be nullptr or a collapsed
* range. Ranges are converted to strings using their `ToString()` method.
* Whitespace is compressed.
*
* @return The created `TextDirective`, or an error if converting the ranges
* to string fails.
*/
static Result<TextDirective, ErrorResult> CreateTextDirectiveFromRanges(
nsRange* aPrefix, nsRange* aStart, nsRange* aEnd, nsRange* aSuffix);
/**
* Find the length of the common prefix between two folded strings.
*
* @return The length of the common prefix.
*/
static uint32_t FindCommonPrefix(const nsAString& aFoldedStr1,
const nsAString& aFoldedStr2);
/**
* Find the length of the common suffix between two folded strings.
*
* @return The length of the common suffix.
*/
static uint32_t FindCommonSuffix(const nsAString& aFoldedStr1,
const nsAString& aFoldedStr2);
/**
* Map a logical offset to a container node and offset within the DOM.
*
* @param aRange The nsRange to map the offset from.
* @param aLogicalOffset The logical offset in the flattened text content of
* the range. The offset is always starting at the start
* of the range.
* @return a `RangeBoundary` that represents the logical offset, or an error.
*/
static RangeBoundary CreateRangeBoundaryByMovingOffsetFromRangeStart(
nsRange* aRange, uint32_t aLogicalOffset);
};
class TimeoutWatchdog final {
public:
TimeoutWatchdog()
: mStartTime(TimeStamp::Now()),
mDuration(TimeDuration::FromSeconds(
StaticPrefs::
dom_text_fragments_create_text_fragment_timeout_seconds())) {}
bool IsDone() const { return TimeStamp::Now() - mStartTime > mDuration; }
private:
TimeStamp mStartTime;
TimeDuration mDuration;
};
} // namespace mozilla::dom
#endif