diff --git a/Cargo.lock b/Cargo.lock index b274918f528c..5ec0c884a4a1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1494,15 +1494,6 @@ dependencies = [ "bitflags 2.4.1", ] -[[package]] -name = "dom_fragmentdirectives" -version = "0.1.0" -dependencies = [ - "nsstring", - "percent-encoding", - "thin-vec", -] - [[package]] name = "dtoa" version = "0.4.8" @@ -2264,7 +2255,6 @@ dependencies = [ "data_storage", "detect_win32k_conflicts", "dom", - "dom_fragmentdirectives", "encoding_glue", "fallible_collections", "fluent", diff --git a/devtools/server/actors/page-style.js b/devtools/server/actors/page-style.js index b37816c85f41..1783b58a8f73 100644 --- a/devtools/server/actors/page-style.js +++ b/devtools/server/actors/page-style.js @@ -704,7 +704,6 @@ class PageStyleActor extends Actor { case "::first-line": case "::selection": case "::highlight": - case "::target-text": return true; case "::marker": return this._nodeIsListItem(node); diff --git a/dom/base/Document.cpp b/dom/base/Document.cpp index 29a19284618b..d52ac00bb3e7 100644 --- a/dom/base/Document.cpp +++ b/dom/base/Document.cpp @@ -172,8 +172,6 @@ #include "mozilla/dom/FeaturePolicy.h" #include "mozilla/dom/FeaturePolicyUtils.h" #include "mozilla/dom/FontFaceSet.h" -#include "mozilla/dom/FragmentDirective.h" -#include "mozilla/dom/fragmentdirectives_ffi_generated.h" #include "mozilla/dom/FromParser.h" #include "mozilla/dom/HighlightRegistry.h" #include "mozilla/dom/HTMLAllCollection.h" @@ -2485,7 +2483,6 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(Document) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFontFaceSet) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReadyForIdle) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentL10n) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFragmentDirective) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHighlightRegistry) // Traverse all Document nsCOMPtrs. @@ -2633,7 +2630,6 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Document) NS_IMPL_CYCLE_COLLECTION_UNLINK(mFontFaceSet) NS_IMPL_CYCLE_COLLECTION_UNLINK(mReadyForIdle) NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentL10n) - NS_IMPL_CYCLE_COLLECTION_UNLINK(mFragmentDirective) NS_IMPL_CYCLE_COLLECTION_UNLINK(mHighlightRegistry) NS_IMPL_CYCLE_COLLECTION_UNLINK(mParser) NS_IMPL_CYCLE_COLLECTION_UNLINK(mOnloadBlocker) @@ -4068,21 +4064,6 @@ void Document::StopDocumentLoad() { void Document::SetDocumentURI(nsIURI* aURI) { nsCOMPtr oldBase = GetDocBaseURI(); mDocumentURI = aURI; - // This loosely implements §3.4.1 of Text Fragments - // https://wicg.github.io/scroll-to-text-fragment/#invoking-text-directives - // Unlike specified in the spec, the fragment directive is not stripped from - // the URL in the session history entry. Instead it is removed when the URL is - // set in the `Document`. Also, instead of storing the `uninvokedDirective` in - // `Document` as mentioned in the spec, the extracted directives are moved to - // the `FragmentDirective` object which deals with finding the ranges to - // highlight in `ScrollToRef()`. - // XXX(:jjaschke): This is only a temporary solution. - // https://bugzil.la/1881429 is filed for revisiting this. - nsTArray textDirectives; - FragmentDirective::ParseAndRemoveFragmentDirectiveFromFragment( - mDocumentURI, &textDirectives); - FragmentDirective()->SetTextDirectives(std::move(textDirectives)); - nsIURI* newBase = GetDocBaseURI(); mChromeRulesEnabled = URLExtraData::ChromeRulesEnabled(aURI); @@ -13103,29 +13084,25 @@ void Document::SetScrollToRef(nsIURI* aDocumentURI) { // https://html.spec.whatwg.org/#scrolling-to-a-fragment void Document::ScrollToRef() { + if (mScrolledToRefAlready) { + RefPtr presShell = GetPresShell(); + if (presShell) { + presShell->ScrollToAnchor(); + } + return; + } + + // 2. If fragment is the empty string, then return the special value top of + // the document. + if (mScrollToRef.IsEmpty()) { + return; + } + RefPtr presShell = GetPresShell(); if (!presShell) { return; } - 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); - - // 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 (didScrollToTextFragment || mScrollToRef.IsEmpty()) { - return; - } // 3. Let potentialIndicatedElement be the result of finding a potential // indicated element given document and fragment. NS_ConvertUTF8toUTF16 ref(mScrollToRef); @@ -19083,13 +19060,6 @@ HighlightRegistry& Document::HighlightRegistry() { return *mHighlightRegistry; } -FragmentDirective* Document::FragmentDirective() { - if (!mFragmentDirective) { - mFragmentDirective = MakeRefPtr(this); - } - return mFragmentDirective; -} - RadioGroupContainer& Document::OwnedRadioGroupContainer() { if (!mRadioGroupContainer) { mRadioGroupContainer = MakeUnique(); diff --git a/dom/base/Document.h b/dom/base/Document.h index 6948f90f3ef2..290334c1e3a2 100644 --- a/dom/base/Document.h +++ b/dom/base/Document.h @@ -244,7 +244,6 @@ class EventListener; struct FailedCertSecurityInfo; class FeaturePolicy; class FontFaceSet; -class FragmentDirective; class FrameRequestCallback; class ImageTracker; class HighlightRegistry; @@ -4092,13 +4091,6 @@ class Document : public nsINode, */ class HighlightRegistry& HighlightRegistry(); - /** - * @brief Returns the `FragmentDirective` object which contains information - * and functionality to extract or create text directives. - * Guaranteed to be non-null. - */ - class FragmentDirective* FragmentDirective(); - bool ShouldResistFingerprinting(RFPTarget aTarget) const; bool IsInPrivateBrowsing() const; @@ -5377,7 +5369,6 @@ class Document : public nsINode, nsTArray mCanvasUsage; uint64_t mLastCanvasUsage = 0; - RefPtr mFragmentDirective; UniquePtr mRadioGroupContainer; public: diff --git a/dom/base/FragmentDirective.cpp b/dom/base/FragmentDirective.cpp deleted file mode 100644 index 3300a85751b3..000000000000 --- a/dom/base/FragmentDirective.cpp +++ /dev/null @@ -1,879 +0,0 @@ -/* -*- 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 - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "FragmentDirective.h" -#include -#include "RangeBoundary.h" -#include "mozilla/Assertions.h" -#include "Document.h" -#include "mozilla/dom/FragmentDirectiveBinding.h" -#include "mozilla/dom/FragmentOrElement.h" -#include "mozilla/dom/NodeBinding.h" -#include "mozilla/dom/Text.h" -#include "mozilla/intl/WordBreaker.h" -#include "nsComputedDOMStyle.h" -#include "nsContentUtils.h" -#include "nsDOMAttributeMap.h" -#include "nsGkAtoms.h" -#include "nsICSSDeclaration.h" -#include "nsIFrame.h" -#include "nsINode.h" -#include "nsIURIMutator.h" -#include "nsRange.h" -#include "nsString.h" - -namespace mozilla::dom { -static LazyLogModule sFragmentDirectiveLog("FragmentDirective"); - -/** Converts a `TextDirective` into a percent-encoded string. */ -nsCString ToString(const TextDirective& aTextDirective) { - nsCString str; - create_text_directive(&aTextDirective, &str); - return str; -} - -NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(FragmentDirective, mDocument) -NS_IMPL_CYCLE_COLLECTING_ADDREF(FragmentDirective) -NS_IMPL_CYCLE_COLLECTING_RELEASE(FragmentDirective) -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FragmentDirective) - NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY - NS_INTERFACE_MAP_ENTRY(nsISupports) -NS_INTERFACE_MAP_END - -FragmentDirective::FragmentDirective(Document* aDocument) - : mDocument(aDocument) {} - -JSObject* FragmentDirective::WrapObject(JSContext* aCx, - JS::Handle aGivenProto) { - return FragmentDirective_Binding::Wrap(aCx, this, aGivenProto); -} - -void FragmentDirective::ParseAndRemoveFragmentDirectiveFromFragment( - nsCOMPtr& aURI, nsTArray* aTextDirectives) { - if (!aURI || !StaticPrefs::dom_text_fragments_enabled()) { - return; - } - bool hasRef = false; - aURI->GetHasRef(&hasRef); - if (!hasRef) { - return; - } - - nsAutoCString hash; - aURI->GetRef(hash); - - ParsedFragmentDirectiveResult fragmentDirective; - const bool hasRemovedFragmentDirective = - parse_fragment_directive(&hash, &fragmentDirective); - if (!hasRemovedFragmentDirective) { - return; - } - Unused << NS_MutateURI(aURI) - .SetRef(fragmentDirective.url_without_fragment_directive) - .Finalize(aURI); - if (aTextDirectives) { - aTextDirectives->SwapElements(fragmentDirective.text_directives); - } -} - -nsTArray> FragmentDirective::FindTextFragmentsInDocument() { - MOZ_ASSERT(mDocument); - mDocument->FlushPendingNotifications(FlushType::Frames); - nsTArray> textDirectiveRanges; - for (const TextDirective& textDirective : mUninvokedTextDirectives) { - if (RefPtr range = FindRangeForTextDirective(textDirective)) { - textDirectiveRanges.AppendElement(range); - } - } - mUninvokedTextDirectives.Clear(); - return textDirectiveRanges; -} - -/** - * @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 - * - * see https://wicg.github.io/scroll-to-text-fragment/#search-invisible - */ -bool NodeIsSearchInvisible(nsINode& aNode) { - if (!aNode.IsElement()) { - return false; - } - // 2. If the node serializes as void. - nsAtom* nodeNameAtom = aNode.NodeInfo()->NameAtom(); - if (FragmentOrElement::IsHTMLVoid(nodeNameAtom)) { - return true; - } - // 3. Is any of the following types: HTMLIFrameElement, HTMLImageElement, - // HTMLMeterElement, HTMLObjectElement, HTMLProgressElement, HTMLStyleElement, - // HTMLScriptElement, HTMLVideoElement, HTMLAudioElement - if (aNode.IsAnyOfHTMLElements( - nsGkAtoms::iframe, nsGkAtoms::image, nsGkAtoms::meter, - nsGkAtoms::object, nsGkAtoms::progress, nsGkAtoms::style, - nsGkAtoms::script, nsGkAtoms::video, nsGkAtoms::audio)) { - return true; - } - // 4. Is a select element whose multiple content attribute is absent. - if (aNode.IsHTMLElement(nsGkAtoms::select)) { - return aNode.GetAttributes()->GetNamedItem(u"multiple"_ns) == nullptr; - } - // This is tested last because it's the most expensive check. - // 1. The computed value of its 'display' property is 'none'. - const Element* nodeAsElement = Element::FromNode(aNode); - const RefPtr computedStyle = - nsComputedDOMStyle::GetComputedStyleNoFlush(nodeAsElement); - return !computedStyle || - computedStyle->StyleDisplay()->mDisplay == StyleDisplay::None; -} - -/** - * @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 - * - * See https://wicg.github.io/scroll-to-text-fragment/#has-block-level-display - */ -bool NodeHasBlockLevelDisplay(nsINode& aNode) { - if (!aNode.IsElement()) { - return false; - } - const Element* nodeAsElement = Element::FromNode(aNode); - const RefPtr computedStyle = - nsComputedDOMStyle::GetComputedStyleNoFlush(nodeAsElement); - if (!computedStyle) { - return false; - } - const StyleDisplay& styleDisplay = computedStyle->StyleDisplay()->mDisplay; - return styleDisplay == StyleDisplay::Block || - styleDisplay == StyleDisplay::Table || - styleDisplay == StyleDisplay::FlowRoot || - styleDisplay == StyleDisplay::Grid || - styleDisplay == StyleDisplay::Flex || styleDisplay.IsListItem(); -} - -/** - * @brief Get the Block Ancestor For `aNode`. - * - * see https://wicg.github.io/scroll-to-text-fragment/#nearest-block-ancestor - */ -nsINode* GetBlockAncestorForNode(nsINode* aNode) { - // 1. Let curNode be node. - RefPtr curNode = aNode; - // 2. While curNode is non-null - while (curNode) { - // 2.1. If curNode is not a Text node and it has block-level display then - // return curNode. - if (!curNode->IsText() && NodeHasBlockLevelDisplay(*curNode)) { - return curNode; - } - // 2.2. Otherwise, set curNode to curNode’s parent. - curNode = curNode->GetParentNode(); - } - // 3.Return node’s node document's document element. - return aNode->GetOwnerDocument(); -} - -/** - * @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. - * - * see https://wicg.github.io/scroll-to-text-fragment/#non-searchable-subtree - */ -bool NodeIsPartOfNonSearchableSubTree(nsINode& aNode) { - nsINode* node = &aNode; - do { - if (NodeIsSearchInvisible(*node)) { - return true; - } - } while ((node = node->GetParentOrShadowHostNode())); - return false; -} - -/** - * @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. - * - * see https://wicg.github.io/scroll-to-text-fragment/#visible-text-node - */ -bool NodeIsVisibleTextNode(const nsINode& aNode) { - const Text* text = Text::FromNode(aNode); - if (!text) { - return false; - } - const nsIFrame* frame = text->GetPrimaryFrame(); - return frame && frame->StyleVisibility()->IsVisible(); -} - -enum class TextScanDirection { Left = -1, Right = 1 }; - -/** - * @brief Tests if there is whitespace at the given position and direction. - * - * This algorithm tests for whitespaces and ` ` at `aPos`. - * It returns the size of the whitespace found at the position, i.e. 5/6 for - * ` /;` and 1 otherwise. - * - * This function follows a subsection of this section of the spec, but has been - * adapted to be able to scan in both directions: - * https://wicg.github.io/scroll-to-text-fragment/#next-non-whitespace-position - */ -uint32_t IsWhitespaceAtPosition(nsString& aText, uint32_t aPos, - TextScanDirection aDirection) { - if (aText.Length() == 0) { - return 0; - } - if (aDirection == TextScanDirection::Right) { - if (aText.Length() > (aPos + 5)) { - if (Substring(aText, aPos, 5).Equals(u" ")) { - return aText.Length() > (aPos + 6) && aText.CharAt(aPos + 6) == u';' - ? 6 - : 5; - } - } - } else { - if (aPos > 6 && Substring(aText, aPos - 6, 6).Equals(u" ")) { - return 6; - } - if (aPos > 5 && Substring(aText, aPos - 5, 5).Equals(u" ")) { - return 5; - } - } - return uint32_t(IsSpaceCharacter(aText.CharAt(aPos))); -} - -/** Advances the start of `aRange` to the next non-whitespace position. - * The function follows this section of the spec: - * https://wicg.github.io/scroll-to-text-fragment/#next-non-whitespace-position - */ -void AdvanceStartToNextNonWhitespacePosition(nsRange& aRange) { - // 1. While range is not collapsed: - while (!aRange.Collapsed()) { - // 1.1. Let node be range's start node. - RefPtr node = aRange.GetStartContainer(); - MOZ_ASSERT(node); - // 1.2. Let offset be range's start offset. - const uint32_t offset = aRange.StartOffset(); - // 1.3. If node is part of a non-searchable subtree or if node is not a - // visible text node or if offset is equal to node's length then: - if (NodeIsPartOfNonSearchableSubTree(*node) || - !NodeIsVisibleTextNode(*node) || offset == node->Length()) { - // 1.3.1. Set range's start node to the next node, in shadow-including - // tree order. - // 1.3.2. Set range's start offset to 0. - if (NS_FAILED(aRange.SetStart(node->GetNextNode(), 0))) { - return; - } - // 1.3.3. Continue. - continue; - } - const Text* text = Text::FromNode(node); - nsAutoString textData; - text->GetData(textData); - // These steps are moved to `IsWhitespaceAtPosition()`. - // 1.4. If the substring data of node at offset offset and count 6 is equal - // to the string " " then: - // 1.4.1. Add 6 to range’s start offset. - // 1.5. Otherwise, if the substring data of node at offset offset and count - // 5 is equal to the string " " then: - // 1.5.1. Add 5 to range’s start offset. - // 1.6. Otherwise: - // 1.6.1 Let cp be the code point at the offset index in node’s data. - // 1.6.2 If cp does not have the White_Space property set, return. - // 1.6.3 Add 1 to range’s start offset. - const uint32_t whitespace = - IsWhitespaceAtPosition(textData, offset, TextScanDirection::Right); - if (whitespace == 0) { - return; - } - - aRange.SetStart(node, offset + whitespace); - } -} - -/** - * @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. - */ -RangeBoundary MoveRangeBoundaryOneWord(const RangeBoundary& aRangeBoundary, - TextScanDirection aDirection) { - MOZ_ASSERT(aRangeBoundary.IsSetAndValid()); - RefPtr curNode = aRangeBoundary.Container(); - uint32_t offset = *aRangeBoundary.Offset( - RangeBoundary::OffsetFilter::kValidOrInvalidOffsets); - - const int offsetIncrement = int(aDirection); - // Get the text node of the start of the range and the offset. - // This is the current position of the start of the range. - nsAutoString text; - if (NodeIsVisibleTextNode(*curNode)) { - const Text* textNode = Text::FromNode(curNode); - textNode->GetData(text); - - // Assuming that the current position might not be at a word boundary, - // advance to the word boundary at word begin/end. - if (!IsWhitespaceAtPosition(text, offset, aDirection)) { - const intl::WordRange wordRange = - intl::WordBreaker::FindWord(text, offset); - if (aDirection == TextScanDirection::Right && - offset != wordRange.mBegin) { - offset = wordRange.mEnd; - } else if (aDirection == TextScanDirection::Left && - offset != wordRange.mEnd) { - // The additional -1 is necessary to move to offset to *before* the - // start of the word. - offset = wordRange.mBegin - 1; - } - } - } - // Now, skip any whitespace, so that `offset` points to the word boundary of - // the next word (which is the one this algorithm actually aims to move over). - while (curNode) { - if (!NodeIsVisibleTextNode(*curNode) || NodeIsSearchInvisible(*curNode) || - offset >= curNode->Length()) { - curNode = aDirection == TextScanDirection::Left ? curNode->GetPrevNode() - : curNode->GetNextNode(); - if (!curNode) { - break; - } - offset = - aDirection == TextScanDirection::Left ? curNode->Length() - 1 : 0; - if (const Text* textNode = Text::FromNode(curNode)) { - textNode->GetData(text); - } - continue; - } - if (const uint32_t whitespace = - IsWhitespaceAtPosition(text, offset, aDirection)) { - offset += offsetIncrement * whitespace; - continue; - } - - // At this point, the caret has been moved to the next non-whitespace - // position. - // find word boundaries at the current position - const intl::WordRange wordRange = intl::WordBreaker::FindWord(text, offset); - offset = aDirection == TextScanDirection::Left ? wordRange.mBegin - : wordRange.mEnd; - - return {curNode, offset}; - } - return {}; -} - -RefPtr FragmentDirective::FindRangeForTextDirective( - const TextDirective& aTextDirective) { - 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; - RefPtr searchRange = - nsRange::Create(mDocument, 0, mDocument, mDocument->Length(), rv); - if (rv.Failed()) { - return nullptr; - } - // 2. While searchRange is not collapsed: - while (!searchRange->Collapsed()) { - // 2.1. Let potentialMatch be null. - RefPtr potentialMatch; - // 2.2. If parsedValues’s prefix is not null: - if (!aTextDirective.prefix.IsEmpty()) { - // 2.2.1. Let prefixMatch be the the result of running the find a string - // in range steps with query parsedValues’s prefix, searchRange - // searchRange, wordStartBounded true and wordEndBounded false. - RefPtr prefixMatch = - FindStringInRange(searchRange, aTextDirective.prefix, true, false); - // 2.2.2. If prefixMatch is null, return null. - if (!prefixMatch) { - return nullptr; - } - // 2.2.3. Set searchRange’s start to the first boundary point after - // prefixMatch’s start - const RangeBoundary boundaryPoint = MoveRangeBoundaryOneWord( - {prefixMatch->GetStartContainer(), prefixMatch->StartOffset()}, - TextScanDirection::Right); - if (!boundaryPoint.IsSetAndValid()) { - return nullptr; - } - searchRange->SetStart(boundaryPoint.AsRaw(), rv); - if (rv.Failed()) { - return nullptr; - } - - // 2.2.4. Let matchRange be a range whose start is prefixMatch’s end and - // end is searchRange’s end. - RefPtr matchRange = nsRange::Create( - prefixMatch->GetEndContainer(), prefixMatch->EndOffset(), - searchRange->GetEndContainer(), searchRange->EndOffset(), rv); - if (rv.Failed()) { - return nullptr; - } - // 2.2.5. Advance matchRange’s start to the next non-whitespace position. - AdvanceStartToNextNonWhitespacePosition(*matchRange); - // 2.2.6. If matchRange is collapsed return null. - // (This can happen if prefixMatch’s end or its subsequent non-whitespace - // position is at the end of the document.) - if (matchRange->Collapsed()) { - return nullptr; - } - // 2.2.7. Assert: matchRange’s start node is a Text node. - // (matchRange’s start now points to the next non-whitespace text data - // following a matched prefix.) - MOZ_ASSERT(matchRange->GetStartContainer()->IsText()); - - // 2.2.8. Let mustEndAtWordBoundary be true if parsedValues’s end is - // non-null or parsedValues’s suffix is null, false otherwise. - const bool mustEndAtWordBoundary = - !aTextDirective.end.IsEmpty() || aTextDirective.suffix.IsEmpty(); - // 2.2.9. Set potentialMatch to the result of running the find a string in - // range steps with query parsedValues’s start, searchRange matchRange, - // wordStartBounded false, and wordEndBounded mustEndAtWordBoundary. - potentialMatch = FindStringInRange(matchRange, aTextDirective.start, - false, mustEndAtWordBoundary); - // 2.2.10. If potentialMatch is null, return null. - if (!potentialMatch) { - return nullptr; - } - // 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->GetStartContainer() != - matchRange->GetStartContainer()) { - continue; - } - } - // 2.3. Otherwise: - else { - // 2.3.1. Let mustEndAtWordBoundary be true if parsedValues’s end is - // non-null or parsedValues’s suffix is null, false otherwise. - const bool mustEndAtWordBoundary = - !aTextDirective.end.IsEmpty() || aTextDirective.suffix.IsEmpty(); - // 2.3.2. Set potentialMatch to the result of running the find a string in - // range steps with query parsedValues’s start, searchRange searchRange, - // wordStartBounded true, and wordEndBounded mustEndAtWordBoundary. - potentialMatch = FindStringInRange(searchRange, aTextDirective.start, - true, mustEndAtWordBoundary); - // 2.3.3. If potentialMatch is null, return null. - if (!potentialMatch) { - return nullptr; - } - // 2.3.4. Set searchRange’s start to the first boundary point after - // potentialMatch’s start - RangeBoundary newRangeBoundary = MoveRangeBoundaryOneWord( - {potentialMatch->GetStartContainer(), potentialMatch->StartOffset()}, - TextScanDirection::Right); - if (!newRangeBoundary.IsSetAndValid()) { - return nullptr; - } - searchRange->SetStart(newRangeBoundary.AsRaw(), rv); - if (rv.Failed()) { - return nullptr; - } - } - // 2.4. Let rangeEndSearchRange be a range whose start is potentialMatch’s - // end and whose end is searchRange’s end. - RefPtr rangeEndSearchRange = nsRange::Create( - potentialMatch->GetEndContainer(), potentialMatch->EndOffset(), - searchRange->GetEndContainer(), searchRange->EndOffset(), rv); - if (rv.Failed()) { - return nullptr; - } - // 2.5. While rangeEndSearchRange is not collapsed: - while (!rangeEndSearchRange->Collapsed()) { - // 2.5.1. If parsedValues’s end item is non-null, then: - if (!aTextDirective.end.IsEmpty()) { - // 2.5.1.1. Let mustEndAtWordBoundary be true if parsedValues’s suffix - // is null, false otherwise. - const bool mustEndAtWordBoundary = aTextDirective.suffix.IsEmpty(); - // 2.5.1.2. Let endMatch be the result of running the find a string in - // range steps with query parsedValues’s end, searchRange - // rangeEndSearchRange, wordStartBounded true, and wordEndBounded - // mustEndAtWordBoundary. - RefPtr endMatch = - FindStringInRange(rangeEndSearchRange, aTextDirective.end, true, - mustEndAtWordBoundary); - // 2.5.1.3. If endMatch is null then return null. - if (!endMatch) { - return nullptr; - } - // 2.5.1.4. Set potentialMatch’s end to endMatch’s end. - potentialMatch->SetEnd(endMatch->GetEndContainer(), - endMatch->EndOffset()); - } - // 2.5.2. Assert: potentialMatch is non-null, not collapsed and represents - // a range exactly containing an instance of matching text. - MOZ_ASSERT(potentialMatch && !potentialMatch->Collapsed()); - - // 2.5.3. If parsedValues’s suffix is null, return potentialMatch. - if (aTextDirective.suffix.IsEmpty()) { - return potentialMatch; - } - // 2.5.4. Let suffixRange be a range with start equal to potentialMatch’s - // end and end equal to searchRange’s end. - RefPtr suffixRange = nsRange::Create( - potentialMatch->GetEndContainer(), potentialMatch->EndOffset(), - searchRange->GetEndContainer(), searchRange->EndOffset(), rv); - if (rv.Failed()) { - return nullptr; - } - // 2.5.5. Advance suffixRange's start to the next non-whitespace position. - AdvanceStartToNextNonWhitespacePosition(*suffixRange); - - // 2.5.6. Let suffixMatch be result of running the find a string in range - // steps with query parsedValue's suffix, searchRange suffixRange, - // wordStartBounded false, and wordEndBounded true. - RefPtr suffixMatch = - FindStringInRange(suffixRange, aTextDirective.suffix, false, true); - - // 2.5.7. If suffixMatch is null, return null. - // (If the suffix doesn't appear in the remaining text of the document, - // there's no possible way to make a match.) - if (!suffixMatch) { - return nullptr; - } - // 2.5.8. If suffixMatch's start is suffixRange's start, return - // potentialMatch. - if (suffixMatch->GetStartContainer() == - suffixRange->GetStartContainer() && - suffixMatch->StartOffset() == suffixRange->StartOffset()) { - return potentialMatch; - } - // 2.5.9. If parsedValue's end item is null then break; - // (If this is an exact match and the suffix doesn’t match, start - // searching for the next range start by breaking out of this loop without - // rangeEndSearchRange being collapsed. If we’re looking for a range - // match, we’ll continue iterating this inner loop since the range start - // will already be correct.) - if (aTextDirective.end.IsEmpty()) { - break; - } - // 2.5.10. Set rangeEndSearchRange's start to potentialMatch's end. - // (Otherwise, it is possible that we found the correct range start, but - // not the correct range end. Continue the inner loop to keep searching - // for another matching instance of rangeEnd.) - rangeEndSearchRange->SetStart(potentialMatch->GetEndContainer(), - potentialMatch->EndOffset()); - } - // 2.6. If rangeEndSearchRange is collapsed then: - if (rangeEndSearchRange->Collapsed()) { - // 2.6.1. Assert parsedValue's end item is non-null. - // (This can only happen for range matches due to the break for exact - // 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): should this really assert? - MOZ_ASSERT(!aTextDirective.end.IsEmpty()); - } - } - // 3. Return null. - return nullptr; -} - -/** - * @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. - */ -bool IsAtWordBoundary(const nsAString& aText, uint32_t aPosition) { - const intl::WordRange wordRange = - intl::WordBreaker::FindWord(aText, aPosition); - return wordRange.mBegin == aPosition || wordRange.mEnd == aPosition; -} - -enum class IsEndIndex : bool { No, Yes }; -RangeBoundary GetBoundaryPointAtIndex( - uint32_t aIndex, const nsTArray>& aTextNodeList, - IsEndIndex aIsEndIndex) { - // 1. Let counted be 0. - uint32_t counted = 0; - // 2. For each curNode of nodes: - for (Text* curNode : aTextNodeList) { - // 2.1. Let nodeEnd be counted + curNode’s length. - uint32_t nodeEnd = counted + curNode->Length(); - // 2.2. If isEnd is true, add 1 to nodeEnd. - if (aIsEndIndex == IsEndIndex::Yes) { - ++nodeEnd; - } - // 2.3. If nodeEnd is greater than index then: - if (nodeEnd > aIndex) { - // 2.3.1. Return the boundary point (curNode, index − counted). - return RangeBoundary(curNode->AsNode(), aIndex - counted); - } - // 2.4. Increment counted by curNode’s length. - counted += curNode->Length(); - } - return {}; -} - -RefPtr FindRangeFromNodeList( - nsRange* aSearchRange, const nsAString& aQuery, - const nsTArray>& aTextNodeList, bool aWordStartBounded, - bool aWordEndBounded) { - // 1. Let searchBuffer be the concatenation of the data of each item in nodes. - // XXX(:jjaschke): There's an open issue here that deals with what - // data is supposed to be (text data vs. rendered text) - // https://github.com/WICG/scroll-to-text-fragment/issues/98 - uint32_t bufferLength = 0; - for (const Text* text : aTextNodeList) { - bufferLength += text->Length(); - } - // bail out if the search query is longer than the text data. - if (bufferLength < aQuery.Length()) { - return nullptr; - } - nsAutoString searchBuffer; - searchBuffer.SetCapacity(bufferLength); - for (Text* text : aTextNodeList) { - text->AppendTextTo(searchBuffer); - } - // 2. Let searchStart be 0. - // 3. If the first item in nodes is searchRange’s start node then set - // searchStart to searchRange’s start offset. - uint32_t searchStart = - aTextNodeList.SafeElementAt(0) == aSearchRange->GetStartContainer() - ? aSearchRange->StartOffset() - : 0; - - // 4. Let start and end be boundary points, initially null. - RangeBoundary start, end; - // 5. Let matchIndex be null. - // "null" here doesn't mean 0, instead "not set". 0 would be a valid index. - // Therefore, "null" is represented by the value -1. - int32_t matchIndex = -1; - - // 6. While matchIndex is null - // As explained above, "null" == -1 in this algorithm. - while (matchIndex == -1) { - // 6.1. Set matchIndex to the index of the first instance of queryString in - // searchBuffer, starting at searchStart. The string search must be - // performed using a base character comparison, or the primary level, as - // defined in [UTS10]. - // [UTS10] - // Ken Whistler; Markus Scherer.Unicode Collation Algorithm.26 August 2022. - // Unicode Technical Standard #10. - // URL : https://www.unicode.org/reports/tr10/tr10-47.html - - // XXX(:jjaschke): For the initial implementation, a standard case-sensitive - // find-in-string is used. - // See: https://github.com/WICG/scroll-to-text-fragment/issues/233 - matchIndex = searchBuffer.Find(aQuery, searchStart); - // 6.2. If matchIndex is null, return null. - if (matchIndex == -1) { - return nullptr; - } - - // 6.3. Let endIx be matchIndex + queryString’s length. - // endIx is the index of the last character in the match + 1. - const uint32_t endIx = matchIndex + aQuery.Length(); - - // 6.4. Set start to the boundary point result of get boundary point at - // index matchIndex run over nodes with isEnd false. - start = GetBoundaryPointAtIndex(matchIndex, aTextNodeList, IsEndIndex::No); - // 6.5. Set end to the boundary point result of get boundary point at index - // endIx run over nodes with isEnd true. - end = GetBoundaryPointAtIndex(endIx, aTextNodeList, IsEndIndex::Yes); - - // 6.6. If wordStartBounded is true and matchIndex is not at a word boundary - // in searchBuffer, given the language from start’s node as the locale; or - // wordEndBounded is true and matchIndex + queryString’s length is not at a - // word boundary in searchBuffer, given the language from end’s node as the - // locale: - if ((aWordStartBounded && !IsAtWordBoundary(searchBuffer, matchIndex)) || - (aWordEndBounded && !IsAtWordBoundary(searchBuffer, endIx))) { - // 6.6.1. Set searchStart to matchIndex + 1. - searchStart = matchIndex + 1; - // 6.6.2. Set matchIndex to null. - matchIndex = -1; - } - } - // 7. Let endInset be 0. - // 8. If the last item in nodes is searchRange’s end node then set endInset - // to (searchRange’s end node's length − searchRange’s end offset) - // (endInset is the offset from the last position in the last node in the - // reverse direction. Alternatively, it is the length of the node that’s not - // included in the range.) - uint32_t endInset = - aTextNodeList.LastElement() == aSearchRange->GetEndContainer() - ? aSearchRange->GetEndContainer()->Length() - - aSearchRange->EndOffset() - : 0; - - // 9. If matchIndex + queryString’s length is greater than searchBuffer’s - // length − endInset return null. - // (If the match runs past the end of the search range, return null.) - if (matchIndex + aQuery.Length() > searchBuffer.Length() - endInset) { - return nullptr; - } - - // 10. Assert: start and end are non-null, valid boundary points in - // searchRange. - MOZ_ASSERT(start.IsSetAndValid()); - MOZ_ASSERT(end.IsSetAndValid()); - - // 11. Return a range with start start and end end. - ErrorResult rv; - RefPtr range = nsRange::Create(start, end, rv); - if (rv.Failed()) { - return nullptr; - } - - return range; -} - -RefPtr FragmentDirective::FindStringInRange(nsRange* aSearchRange, - const nsAString& aQuery, - bool aWordStartBounded, - bool aWordEndBounded) { - MOZ_ASSERT(aSearchRange); - RefPtr searchRange = aSearchRange->CloneRange(); - // 1. While searchRange is not collapsed - while (searchRange && !searchRange->Collapsed()) { - // 1.1. Let curNode be searchRange’s start node. - RefPtr 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 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 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 blockAncestor = GetBlockAncestorForNode(curNode); - - // 1.5. Let textNodeList be a list of Text nodes, initially empty. - nsTArray> 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 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 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); - } - - // 2. Return null. - return nullptr; -} -} // namespace mozilla::dom diff --git a/dom/base/FragmentDirective.h b/dom/base/FragmentDirective.h deleted file mode 100644 index 8972556d6ca1..000000000000 --- a/dom/base/FragmentDirective.h +++ /dev/null @@ -1,111 +0,0 @@ -/* -*- 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 - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef DOM_FRAGMENTDIRECTIVE_H_ -#define DOM_FRAGMENTDIRECTIVE_H_ - -#include "js/TypeDecls.h" -#include "mozilla/dom/BindingDeclarations.h" - -#include "mozilla/dom/fragmentdirectives_ffi_generated.h" -#include "nsCycleCollectionParticipant.h" -#include "nsStringFwd.h" -#include "nsWrapperCache.h" - -class nsINode; -class nsIURI; -class nsRange; -namespace mozilla::dom { -class Document; -class Text; - -/** - * @brief The `FragmentDirective` class is the C++ representation of the - * `Document.fragmentDirective` webidl property. - * - * This class also serves as the main interface to interact with the fragment - * directive from the C++ side. It allows to find text fragment ranges from a - * given list of `TextDirective`s using - * `FragmentDirective::FindTextFragmentsInDocument()`. - * To avoid Text Directives being applied multiple times, this class implements - * the `uninvoked directive` mechanism, which in the spec is defined to be part - * of the `Document` [0]. - * - * [0] - * https://wicg.github.io/scroll-to-text-fragment/#document-uninvoked-directives - */ -class FragmentDirective final : public nsISupports, public nsWrapperCache { - public: - NS_DECL_CYCLE_COLLECTING_ISUPPORTS - NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(FragmentDirective) - - public: - explicit FragmentDirective(Document* aDocument); - FragmentDirective(Document* aDocument, - nsTArray&& aTextDirectives) - : mDocument(aDocument), - mUninvokedTextDirectives(std::move(aTextDirectives)) {} - - protected: - ~FragmentDirective() = default; - - public: - Document* GetParentObject() const { return mDocument; }; - - JSObject* WrapObject(JSContext* aCx, - JS::Handle aGivenProto) override; - - /** - * @brief Sets Text Directives as "uninvoked directive". - */ - void SetTextDirectives(nsTArray&& aTextDirectives) { - mUninvokedTextDirectives = std::move(aTextDirectives); - } - - /** Returns true if there are Text Directives that have not been applied to - * the `Document`. - */ - bool HasUninvokedDirectives() const { - return !mUninvokedTextDirectives.IsEmpty(); - }; - - /** Searches for the current uninvoked text directives and creates a range for - * each one that is found. - * - * When this method returns, the uninvoked directives for this document are - * cleared. - * - * This method tries to follow the specification as close as possible in how - * to find a matching range for a text directive. However, instead of using - * collator-based search, a standard case-insensitive search is used - * (`nsString::find()`). - */ - nsTArray> FindTextFragmentsInDocument(); - - /** Utility function which parses the fragment directive and removes it from - * the hash of the given URI. This operation happens in-place. - * - * If aTextDirectives is nullptr, the parsed fragment directive is discarded. - */ - static void ParseAndRemoveFragmentDirectiveFromFragment( - nsCOMPtr& aURI, - nsTArray* aTextDirectives = nullptr); - - private: - RefPtr FindRangeForTextDirective( - const TextDirective& aTextDirective); - RefPtr FindStringInRange(nsRange* aSearchRange, - const nsAString& aQuery, - bool aWordStartBounded, - bool aWordEndBounded); - - RefPtr mDocument; - nsTArray mUninvokedTextDirectives; -}; - -} // namespace mozilla::dom - -#endif // DOM_FRAGMENTDIRECTIVE_H_ diff --git a/dom/base/Location.cpp b/dom/base/Location.cpp index 9edb9e0b6f8e..1208e753b32a 100644 --- a/dom/base/Location.cpp +++ b/dom/base/Location.cpp @@ -34,7 +34,6 @@ #include "mozilla/Unused.h" #include "mozilla/dom/Document.h" #include "mozilla/dom/DocumentInlines.h" -#include "mozilla/dom/FragmentDirective.h" #include "mozilla/dom/LocationBinding.h" #include "mozilla/dom/ScriptSettings.h" #include "ReferrerInfo.h" @@ -106,9 +105,6 @@ nsresult Location::GetURI(nsIURI** aURI, bool aGetInnermostURI) { } NS_ASSERTION(uri, "nsJARURI screwed up?"); - - // Remove the fragment directive from the url hash. - FragmentDirective::ParseAndRemoveFragmentDirectiveFromFragment(uri); nsCOMPtr exposableURI = net::nsIOService::CreateExposableURI(uri); exposableURI.forget(aURI); return NS_OK; diff --git a/dom/base/fragmentdirectives/Cargo.toml b/dom/base/fragmentdirectives/Cargo.toml deleted file mode 100644 index 7b3b58966823..000000000000 --- a/dom/base/fragmentdirectives/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "dom_fragmentdirectives" -version = "0.1.0" -authors = ["Jan Jaeschke "] -edition = "2021" -license = "MPL-2.0" - -[dependencies] -nsstring = { path = "../../../xpcom/rust/nsstring/" } -thin-vec = { version = "0.2.1", features = ["gecko-ffi"] } -percent-encoding = { version = "2.3.1" } -[lib] -path = "lib.rs" diff --git a/dom/base/fragmentdirectives/cbindgen.toml b/dom/base/fragmentdirectives/cbindgen.toml deleted file mode 100644 index ec54ebc02d13..000000000000 --- a/dom/base/fragmentdirectives/cbindgen.toml +++ /dev/null @@ -1,15 +0,0 @@ -header = """/* 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 - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */""" -autogen_warning = """/* DO NOT MODIFY THIS MANUALLY! This file was generated using cbindgen. See RunCbindgen.py */ -""" -include_version = true -braces = "SameLine" -line_length = 100 -tab_width = 2 -language = "C++" -include_guard = "fragmentdirectives_ffi_generated_h" -includes = ["nsStringFwd.h", "nsTArrayForwardDeclare.h"] - -[export.rename] -"ThinVec" = "nsTArray" diff --git a/dom/base/fragmentdirectives/fragment_directive_impl.rs b/dom/base/fragmentdirectives/fragment_directive_impl.rs deleted file mode 100644 index dfbdb3741558..000000000000 --- a/dom/base/fragmentdirectives/fragment_directive_impl.rs +++ /dev/null @@ -1,342 +0,0 @@ -/* 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 -* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use percent_encoding::{percent_decode, percent_encode, NON_ALPHANUMERIC}; -use std::str; - -/// The `FragmentDirectiveParameter` represents one of -/// `[prefix-,]start[,end][,-suffix]` without any surrounding `-` or `,`. -/// -/// The token is stored as percent-decoded string. -/// Therefore, interfaces exist to -/// - create a `FragmentDirectiveParameter` from a percent-encoded string. -/// This function will determine from occurrence and position of a dash -/// if the token represents a `prefix`, `suffix` or either `start` or `end`. -/// - create a percent-encoded string from the value the token holds. -pub enum TextDirectiveParameter { - Prefix(String), - StartOrEnd(String), - Suffix(String), -} - -impl TextDirectiveParameter { - /// Creates a token from a percent-encoded string. - /// Based on position of a dash the correct token type is determined. - /// Returns `None` in case of an ill-formed token: - /// - starts and ends with a dash (i.e. `-token-`) - /// - only consists of a dash (i.e. `-`) or is empty - /// - conversion from percent-encoded string to utf8 fails. - pub fn from_percent_encoded(token: &[u8]) -> Option { - if token.is_empty() { - return None; - } - let starts_with_dash = *token.first().unwrap() == b'-'; - let ends_with_dash = *token.last().unwrap() == b'-'; - if starts_with_dash && ends_with_dash { - // `-token-` is not valid. - return None; - } - if token.len() == 1 && starts_with_dash { - // `-` is not valid. - return None; - } - // Note: Trimming of the raw strings is currently not mentioned in the spec. - // However, it looks as it is implicitly expected. - if starts_with_dash { - if let Ok(decoded_suffix) = percent_decode(&token[1..]).decode_utf8() { - return Some(TextDirectiveParameter::Suffix(String::from( - decoded_suffix.trim(), - ))); - } - return None; - } - if ends_with_dash { - if let Ok(decoded_prefix) = percent_decode(&token[..token.len() - 1]).decode_utf8() { - return Some(TextDirectiveParameter::Prefix(String::from( - decoded_prefix.trim(), - ))); - } - return None; - } - if let Ok(decoded_text) = percent_decode(&token).decode_utf8() { - return Some(TextDirectiveParameter::StartOrEnd(String::from( - decoded_text.trim(), - ))); - } - None - } - - /// Returns the value of the token as percent-decoded `String`. - pub fn value(&self) -> &String { - match self { - TextDirectiveParameter::Prefix(value) => &value, - TextDirectiveParameter::StartOrEnd(value) => &value, - TextDirectiveParameter::Suffix(value) => &value, - } - } - - /// Creates a percent-encoded string of the token's value. - /// This includes placing a dash appropriately - /// to indicate whether this token is prefix, suffix or start/end. - /// - /// This method always returns a new object. - pub fn to_percent_encoded_string(&self) -> String { - let encode = |text: &String| percent_encode(text.as_bytes(), NON_ALPHANUMERIC).to_string(); - match self { - Self::Prefix(text) => encode(text) + "-", - Self::StartOrEnd(text) => encode(text), - Self::Suffix(text) => { - let encoded = encode(text); - let mut result = String::with_capacity(encoded.len() + 1); - result.push_str("-"); - result.push_str(&encoded); - result - } - } - } -} - -/// This struct represents one parsed text directive using Rust types. -/// -/// A text fragment is encoded into a URL fragment like this: -/// `text=[prefix-,]start[,end][,-suffix]` -/// -/// The text directive is considered valid if at least `start` is not None. -/// (see `Self::is_valid()`). -#[derive(Default)] -pub struct TextDirective { - prefix: Option, - start: Option, - end: Option, - suffix: Option, -} -impl TextDirective { - /// Creates an instance from string parts. - /// This function is intended to be used when a fragment directive string should be created. - /// Returns `None` if `start` is empty. - pub fn from_parts(prefix: String, start: String, end: String, suffix: String) -> Option { - if !start.is_empty() { - Some(Self { - prefix: if !prefix.is_empty() { - Some(TextDirectiveParameter::Prefix(prefix.trim().into())) - } else { - None - }, - start: Some(TextDirectiveParameter::StartOrEnd(start.trim().into())), - end: if !end.is_empty() { - Some(TextDirectiveParameter::StartOrEnd(end.trim().into())) - } else { - None - }, - suffix: if !suffix.is_empty() { - Some(TextDirectiveParameter::Suffix(suffix.trim().into())) - } else { - None - }, - }) - } else { - None - } - } - - /// Creates an instance from a percent-encoded string - /// that originates from a fragment directive. - /// - /// `text_fragment` is supposed to have this format: - /// ``` - /// text=[prefix-,]start[,end][,-suffix] - /// ``` - /// This function returns `None` if `text_fragment` - /// does not start with `text=`, it contains 0 or more - /// than 4 elements or prefix/suffix/start or end - /// occur too many times. - /// It also returns `None` if any of the tokens parses to fail. - pub fn from_percent_encoded_string(text_directive: &str) -> Option { - // first check if the string starts with `text=` - if text_directive.len() < 6 { - return None; - } - if !text_directive.starts_with("text=") { - return None; - } - - let mut parsed_text_directive = Self::default(); - let valid = text_directive[5..] - .split(",") - // Parse the substrings into `TextDirectiveParameter`s. This will determine - // for each substring if it is a Prefix, Suffix or Start/End, - // or if it is invalid. - .map(|token| TextDirectiveParameter::from_percent_encoded(token.as_bytes())) - // populate `parsed_text_directive` and check its validity by inserting the parameters - // one by one. Given that the parameters are sorted by their position in the source, - // the validity of the text directive can be determined while adding the parameters. - .map(|token| match token { - Some(TextDirectiveParameter::Prefix(..)) => { - if !parsed_text_directive.is_empty() { - // `prefix-` must be the first result. - return false; - } - parsed_text_directive.prefix = token; - return true; - } - Some(TextDirectiveParameter::StartOrEnd(..)) => { - if parsed_text_directive.suffix.is_some() { - // start or end must come before `-suffix`. - return false; - } - if parsed_text_directive.start.is_none() { - parsed_text_directive.start = token; - return true; - } - if parsed_text_directive.end.is_none() { - parsed_text_directive.end = token; - return true; - } - // if `start` and `end` is already filled, - // this is invalid as well. - return false; - } - Some(TextDirectiveParameter::Suffix(..)) => { - if parsed_text_directive.start.is_some() - && parsed_text_directive.suffix.is_none() - { - // `start` must be present and `-suffix` must not be present. - // `end` may be present. - parsed_text_directive.suffix = token; - return true; - } - return false; - } - // empty or invalid token renders the whole text directive invalid. - None => false, - }) - .all(|valid| valid); - if valid { - return Some(parsed_text_directive); - } - None - } - - /// Creates a percent-encoded string for the current `TextDirective`. - /// In the unlikely case that the `TextDirective` is invalid (i.e. `start` is None), - /// which should have been caught earlier,this method returns an empty string. - pub fn to_percent_encoded_string(&self) -> String { - if !self.is_valid() { - return String::default(); - } - String::from("text=") - + &[&self.prefix, &self.start, &self.end, &self.suffix] - .iter() - .filter_map(|&token| token.as_ref()) - .map(|token| token.to_percent_encoded_string()) - .collect::>() - .join(",") - } - - pub fn start(&self) -> &Option { - &self.start - } - - pub fn end(&self) -> &Option { - &self.end - } - - pub fn prefix(&self) -> &Option { - &self.prefix - } - - pub fn suffix(&self) -> &Option { - &self.suffix - } - - fn is_empty(&self) -> bool { - self.prefix.is_none() && self.start.is_none() && self.end.is_none() && self.suffix.is_none() - } - - /// A `TextDirective` object is valid if it contains the `start` token. - /// All other tokens are optional. - fn is_valid(&self) -> bool { - self.start.is_some() - } -} -/// Parses a fragment directive into a list of `TextDirective` objects and removes -/// the fragment directive from the input url. -/// -/// If the hash does not contain a fragment directive, `url` is not modified -/// and this function returns `None`. -/// Otherwise, the fragment directive is removed from `url` and parsed. -/// If parsing fails, this function returns `None`. -pub fn parse_fragment_directive_and_remove_it_from_hash( - url: &str, -) -> Option<(&str, &str, Vec)> { - // The Fragment Directive is preceded by a `:~:`, - // which is only allowed to appear in the hash once. - // However (even if unlikely), it might appear outside of the hash, - // so this code only considers it when it is after the #. - let maybe_first_hash_pos = url.find("#"); - // If there is no # in url, it is considered to be only the hash (and not a full url). - let first_hash_pos = maybe_first_hash_pos.unwrap_or_default(); - let mut fragment_directive_iter = url[first_hash_pos..].split(":~:"); - let url_with_stripped_fragment_directive = - &url[..first_hash_pos + fragment_directive_iter.next().unwrap_or_default().len()]; - - if let Some(fragment_directive) = fragment_directive_iter.next() { - if fragment_directive_iter.next().is_some() { - // There are multiple occurrences of `:~:`, which is not allowed. - return None; - } - // - fragments are separated by `&`. - // - if a fragment does not start with `text=`, it is not a text fragment and will be ignored. - // - if parsing of the text fragment fails (for whatever reason), it will be ignored. - let text_directives: Vec<_> = fragment_directive - .split("&") - .map(|maybe_text_fragment| { - TextDirective::from_percent_encoded_string(&maybe_text_fragment) - }) - .filter_map(|maybe_text_directive| maybe_text_directive) - .collect(); - if !text_directives.is_empty() { - return Some(( - url_with_stripped_fragment_directive - .strip_suffix("#") - .unwrap_or(url_with_stripped_fragment_directive), - fragment_directive, - text_directives, - )); - } - } - None -} - -/// Creates a percent-encoded text fragment string. -/// -/// The returned string starts with `:~:`, so that it can be appended -/// to a normal fragment. -/// Text directives which are not valid (ie., they are missing the `start` parameter), -/// are skipped. -/// -/// Returns `None` if `fragment_directives` is empty. -pub fn create_fragment_directive_string(text_directives: &Vec) -> Option { - if text_directives.is_empty() { - return None; - } - let encoded_fragment_directives: Vec<_> = text_directives - .iter() - .filter(|&fragment_directive| fragment_directive.is_valid()) - .map(|fragment_directive| fragment_directive.to_percent_encoded_string()) - .filter(|text_directive| !text_directive.is_empty()) - .collect(); - if encoded_fragment_directives.is_empty() { - return None; - } - Some(String::from(":~:") + &encoded_fragment_directives.join("&")) -} - -/// Creates the percent-encoded text directive string for a single text directive. -pub fn create_text_directive_string(text_directive: &TextDirective) -> Option { - if text_directive.is_valid() { - Some(text_directive.to_percent_encoded_string()) - } else { - None - } -} diff --git a/dom/base/fragmentdirectives/lib.rs b/dom/base/fragmentdirectives/lib.rs deleted file mode 100644 index 0003849eb751..000000000000 --- a/dom/base/fragmentdirectives/lib.rs +++ /dev/null @@ -1,158 +0,0 @@ -/* 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 - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -use nsstring::{nsCString, nsString}; -use thin_vec::ThinVec; -pub mod fragment_directive_impl; -mod test; - -/// This struct contains the percent-decoded parts of a text directive. -/// All parts besides `start` are optional (which is indicated by an empty string). -/// -/// This struct uses Gecko String types, whereas the parser internally uses Rust types. -/// Therefore, conversion functions are provided. -#[repr(C)] -pub struct TextDirective { - prefix: nsString, - start: nsString, - end: nsString, - suffix: nsString, -} - -impl TextDirective { - /// Creates a `FragmentDirectiveElement` object from a `FragmentDirectiveElementInternal` object - /// (which uses Rust string types). - fn from_rust_type(element: &fragment_directive_impl::TextDirective) -> Self { - Self { - prefix: element - .prefix() - .as_ref() - .map_or_else(nsString::new, |token| nsString::from(token.value())), - start: element - .start() - .as_ref() - .map_or_else(nsString::new, |token| nsString::from(token.value())), - end: element - .end() - .as_ref() - .map_or_else(nsString::new, |token| nsString::from(token.value())), - suffix: element - .suffix() - .as_ref() - .map_or_else(nsString::new, |token| nsString::from(token.value())), - } - } - - /// Converts the contents of this object into Rust types. - /// Returns `None` if the given fragment is not valid. - /// The only invalid condition is a fragment that is missing the `start` token. - fn to_rust_type(&self) -> Option { - fragment_directive_impl::TextDirective::from_parts( - self.prefix.to_string(), - self.start.to_string(), - self.end.to_string(), - self.suffix.to_string(), - ) - } -} - -/// Result of the `parse_fragment_directive()` function. -/// -/// The result contains the original given URL without the fragment directive, -/// a unsanitized string version of the extracted fragment directive, -/// and an array of the parsed text directives. -#[repr(C)] -pub struct ParsedFragmentDirectiveResult { - url_without_fragment_directive: nsCString, - fragment_directive: nsCString, - text_directives: ThinVec, -} - -/// Parses the fragment directive from a given URL. -/// -/// This function writes the result data into `result`. -/// The result consists of -/// - the input url without the fragment directive, -/// - the fragment directive as unparsed string, -/// - a list of the parsed and percent-decoded text directives. -/// -/// Directives which are unknown will be ignored. -/// If new directive types are added in the future, they should also be considered here. -/// This function returns false if no fragment directive is found, or it could not be parsed. -#[no_mangle] -pub extern "C" fn parse_fragment_directive( - url: &nsCString, - result: &mut ParsedFragmentDirectiveResult, -) -> bool { - // sanitize inputs - result.url_without_fragment_directive = nsCString::new(); - result.fragment_directive = nsCString::new(); - result.text_directives.clear(); - - let url_as_rust_string = url.to_utf8(); - if let Some((stripped_url, fragment_directive, text_directives)) = - fragment_directive_impl::parse_fragment_directive_and_remove_it_from_hash( - &url_as_rust_string, - ) - { - result - .url_without_fragment_directive - .assign(&stripped_url); - result.fragment_directive.assign(&fragment_directive); - result.text_directives.extend( - text_directives - .iter() - .map(|text_directive| TextDirective::from_rust_type(text_directive)), - ); - return true; - } - false -} - -/// Creates a percent-encoded fragment directive string from a given list of `FragmentDirectiveElement`s. -/// -/// The returned string has this form: -/// `:~:text=[prefix1-,]start1[,end1][,-suffix1]&text=[prefix2-,]start2[,end2][,-suffix2]` -/// -/// Invalid `FragmentDirectiveElement`s are ignored, where "invalid" means that no `start` token is provided. -/// If there are no valid `FragmentDirectiveElement`s, an empty string is returned. -#[no_mangle] -pub extern "C" fn create_fragment_directive( - text_directives: &ThinVec, - fragment_directive: &mut nsCString, -) -> bool { - let directives_rust = Vec::from_iter( - text_directives - .iter() - .filter_map(|fragment| fragment.to_rust_type()), - ); - if let Some(fragment_directive_rust) = - fragment_directive_impl::create_fragment_directive_string(&directives_rust) - { - fragment_directive.assign(&fragment_directive_rust); - return true; - } - - false -} - -/// Creates a percent-encoded text directive string for a single text directive. -/// The returned string has the form `text=[prefix-,]start[,end][,-suffix]`. -/// If the provided `TextDirective` is invalid (i.e. it has no `start` attribute), -/// the outparam `directive_string` is empty and the function returns false. -#[no_mangle] -pub extern "C" fn create_text_directive( - text_directive: &TextDirective, - directive_string: &mut nsCString, -) -> bool { - if let Some(text_directive_rust) = text_directive.to_rust_type() { - if let Some(text_directive_string_rust) = - fragment_directive_impl::create_text_directive_string(&text_directive_rust) - { - directive_string.assign(&text_directive_string_rust); - return true; - } - } - false -} diff --git a/dom/base/fragmentdirectives/test.rs b/dom/base/fragmentdirectives/test.rs deleted file mode 100644 index d4509cb0335b..000000000000 --- a/dom/base/fragmentdirectives/test.rs +++ /dev/null @@ -1,599 +0,0 @@ -/* 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 - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#[cfg(test)] -mod test { - use crate::fragment_directive_impl::{ - create_fragment_directive_string, parse_fragment_directive_and_remove_it_from_hash, - TextDirective, - }; - - /// This test verifies that valid combinations of [prefix-,]start[,end][,-suffix] are parsed correctly. - #[test] - fn test_parse_fragment_directive_with_one_text_directive() { - let test_cases = vec![ - ("#:~:text=start", (None, Some("start"), None, None)), - ( - "#:~:text=start,end", - (None, Some("start"), Some("end"), None), - ), - ( - "#:~:text=prefix-,start", - (Some("prefix"), Some("start"), None, None), - ), - ( - "#:~:text=prefix-,start,end", - (Some("prefix"), Some("start"), Some("end"), None), - ), - ( - "#:~:text=prefix-,start,end,-suffix", - (Some("prefix"), Some("start"), Some("end"), Some("suffix")), - ), - ( - "#:~:text=start,-suffix", - (None, Some("start"), None, Some("suffix")), - ), - ( - "#:~:text=start,end,-suffix", - (None, Some("start"), Some("end"), Some("suffix")), - ), - ("#:~:text=text=", (None, Some("text="), None, None)), - ]; - for (url, (prefix, start, end, suffix)) in test_cases { - let (stripped_url, fragment_directive, result) = - parse_fragment_directive_and_remove_it_from_hash(&url) - .expect("The parser must find a result."); - assert_eq!( - fragment_directive, - &url[4..], - "The extracted fragment directive string - should be unsanitized and therefore match the input string." - ); - assert_eq!(result.len(), 1, "There must be one parsed text fragment."); - assert_eq!( - stripped_url, "", - "The fragment directive must be removed from the url hash." - ); - let text_directive = result.first().unwrap(); - if prefix.is_none() { - assert!( - text_directive.prefix().is_none(), - "There must be no `prefix` token (test case `{}`).", - url - ); - } else { - assert!( - text_directive - .prefix() - .as_ref() - .expect("There must be a `prefix` token.") - .value() - == prefix.unwrap(), - "Wrong value for `prefix` (test case `{}`).", - url - ); - } - if start.is_none() { - assert!( - text_directive.start().is_none(), - "There must be no `start` token (test case `{}`).", - url - ); - } else { - assert!( - text_directive - .start() - .as_ref() - .expect("There must be a `start` token.") - .value() - == start.unwrap(), - "Wrong value for `start` (test case `{}`).", - url - ); - } - if end.is_none() { - assert!( - text_directive.end().is_none(), - "There must be no `end` token (test case `{}`).", - url - ); - } else { - assert!( - text_directive - .end() - .as_ref() - .expect("There must be a `end` token.") - .value() - == end.unwrap(), - "Wrong value for `end` (test case `{}`).", - url - ); - } - if suffix.is_none() { - assert!( - text_directive.suffix().is_none(), - "There must be no `suffix` token (test case `{}`).", - url - ); - } else { - assert!( - text_directive - .suffix() - .as_ref() - .expect("There must be a `suffix` token.") - .value() - == suffix.unwrap(), - "Wrong value for `suffix` (test case `{}`).", - url - ); - } - } - } - - #[test] - fn test_parse_full_url() { - for (url, stripped_url_ref) in [ - ("https://example.com#:~:text=foo", "https://example.com"), - ( - "https://example.com/some/page.html?query=answer#:~:text=foo", - "https://example.com/some/page.html?query=answer", - ), - ( - "https://example.com/some/page.html?query=answer#fragment:~:text=foo", - "https://example.com/some/page.html?query=answer#fragment", - ), - ( - "http://example.com/page.html?query=irrelevant:~:#bar:~:text=foo", - "http://example.com/page.html?query=irrelevant:~:#bar" - ) - ] { - let (stripped_url, fragment_directive, _) = - parse_fragment_directive_and_remove_it_from_hash(&url) - .expect("The parser must find a result"); - assert_eq!(stripped_url, stripped_url_ref, "The stripped url is not correct."); - assert_eq!(fragment_directive, "text=foo"); - } - } - - /// This test verifies that a text fragment is parsed correctly if it is preceded - /// or followed by a fragment (i.e. `#foo:~:text=bar`). - #[test] - fn test_parse_text_fragment_after_fragments() { - let url = "#foo:~:text=start"; - let (stripped_url, fragment_directive, result) = - parse_fragment_directive_and_remove_it_from_hash(&url) - .expect("The parser must find a result."); - assert_eq!( - result.len(), - 1, - "There must be exactly one parsed text fragment." - ); - assert_eq!( - stripped_url, "#foo", - "The fragment directive was not removed correctly." - ); - assert_eq!( - fragment_directive, "text=start", - "The fragment directive was not extracted correctly." - ); - let fragment = result.first().unwrap(); - assert!(fragment.prefix().is_none(), "There is no `prefix` token."); - assert_eq!( - fragment - .start() - .as_ref() - .expect("There must be a `start` token.") - .value(), - "start" - ); - assert!(fragment.end().is_none(), "There is no `end` token."); - assert!(fragment.suffix().is_none(), "There is no `suffix` token."); - } - - /// Ensure that multiple text fragments are parsed correctly. - #[test] - fn test_parse_multiple_text_fragments() { - let url = "#:~:text=prefix-,start,-suffix&text=foo&text=bar,-suffix"; - let (_, _, text_directives) = - parse_fragment_directive_and_remove_it_from_hash(&url) - .expect("The parser must find a result."); - assert_eq!( - text_directives.len(), - 3, - "There must be exactly two parsed text fragments." - ); - let first_text_directive = &text_directives[0]; - assert_eq!( - first_text_directive - .prefix() - .as_ref() - .expect("There must be a `prefix` token.") - .value(), - "prefix" - ); - assert_eq!( - first_text_directive - .start() - .as_ref() - .expect("There must be a `start` token.") - .value(), - "start" - ); - assert!( - first_text_directive.end().is_none(), - "There is no `end` token." - ); - assert_eq!( - first_text_directive - .suffix() - .as_ref() - .expect("There must be a `suffix` token.") - .value(), - "suffix" - ); - - let second_text_directive = &text_directives[1]; - assert!( - second_text_directive.prefix().is_none(), - "There is no `prefix` token." - ); - assert_eq!( - second_text_directive - .start() - .as_ref() - .expect("There must be a `start` token.") - .value(), - "foo" - ); - assert!( - second_text_directive.end().is_none(), - "There is no `end` token." - ); - assert!( - second_text_directive.suffix().is_none(), - "There is no `suffix` token." - ); - let third_text_directive = &text_directives[2]; - assert!( - third_text_directive.prefix().is_none(), - "There is no `prefix` token." - ); - assert_eq!( - third_text_directive - .start() - .as_ref() - .expect("There must be a `start` token.") - .value(), - "bar" - ); - assert!( - third_text_directive.end().is_none(), - "There is no `end` token." - ); - assert_eq!( - third_text_directive - .suffix() - .as_ref() - .expect("There must be a `suffix` token.") - .value(), - "suffix" - ); - } - - /// Multiple text directives should be parsed correctly - /// if they are surrounded or separated by unknown directives. - #[test] - fn test_parse_multiple_text_directives_with_unknown_directive_in_between() { - for url in [ - "#:~:foo&text=start1&text=start2", - "#:~:text=start1&foo&text=start2", - "#:~:text=start1&text=start2&foo", - ] { - let (_, fragment_directive, text_directives) = - parse_fragment_directive_and_remove_it_from_hash(&url) - .expect("The parser must find a result."); - assert_eq!( - fragment_directive, - &url[4..], - "The extracted fragment directive string is unsanitized - and should contain the unknown directive." - ); - assert_eq!( - text_directives.len(), - 2, - "There must be exactly two parsed text fragments." - ); - let first_text_directive = &text_directives[0]; - assert_eq!( - first_text_directive - .start() - .as_ref() - .expect("There must be a `start` token.") - .value(), - "start1" - ); - let second_text_directive = &text_directives[1]; - assert_eq!( - second_text_directive - .start() - .as_ref() - .expect("There must be a `start` token.") - .value(), - "start2" - ); - } - } - - /// Ensures that input that doesn't contain a text fragment does not produce a result. - /// This includes the use of partial identifying tokens necessary for a text fragment - /// (e.g. `:~:` without `text=`, `text=foo` without the `:~:` or multiple occurrences of `:~:`) - /// In these cases, the parser must return `None` to indicate that there are no valid text fragments. - #[test] - fn test_parse_invalid_or_unknown_fragment_directive() { - for url in [ - "#foo", - "#foo:", - "#foo:~:", - "#foo:~:bar", - "text=prefix-,start", - "#:~:text=foo-,bar,-baz:~:text=foo", - ] { - let text_directives = - parse_fragment_directive_and_remove_it_from_hash(&url); - assert!( - text_directives.is_none(), - "The fragment `{}` does not contain a valid or known fragment directive.", - url - ); - } - } - - /// Ensures that ill-formed text directives (but valid fragment directives) - /// (starting correctly with `:~:text=`) are not parsed. - /// Instead `None` must be returned. - /// Test cases include invalid combinations of `prefix`/`suffix`es, - /// additional `,`s, too many `start`/`end` tokens, or empty text fragments. - #[test] - fn test_parse_invalid_text_fragments() { - for url in [ - "#:~:text=start,start,start", - "#:~:text=prefix-,prefix-", - "#:~:text=prefix-,-suffix", - "#:~:text=prefix-,start,start,start", - "#:~:text=prefix-,start,start,start,-suffix", - "#:~:text=start,start,start,-suffix", - "#:~:text=prefix-,start,end,-suffix,foo", - "#:~:text=foo,prefix-,start", - "#:~:text=prefix-,,start,", - "#:~:text=,prefix,start", - "#:~:text=", - ] { - let text_directives = - parse_fragment_directive_and_remove_it_from_hash(&url); - assert!( - text_directives.is_none(), - "The fragment directive `{}` does not contain a valid text directive.", - url - ); - } - } - - /// Ensure that out of multiple text fragments only the invalid ones are ignored - /// while valid text fragments are still returned. - /// Since correct parsing of multiple text fragments as well as - /// several forms of invalid text fragments are already tested in - /// `test_parse_multiple_text_fragments` and `test_parse_invalid_text_fragments()`, - /// it should be enough to test this with only one fragment directive - /// that contains two text fragments, one of them being invalid. - #[test] - fn test_valid_and_invalid_text_directives() { - for url in [ - "#:~:text=start&text=,foo,", - "#:~:text=foo,foo,foo&text=start", - ] { - let (_, fragment_directive, text_directives) = - parse_fragment_directive_and_remove_it_from_hash(&url) - .expect("The parser must find a result."); - assert_eq!( - fragment_directive, - &url[4..], - "The extracted fragment directive string is unsanitized - and should contain invalid text directives." - ); - assert_eq!( - text_directives.len(), - 1, - "There must be exactly one parsed text fragment." - ); - let text_directive = text_directives.first().unwrap(); - assert_eq!( - text_directive - .start() - .as_ref() - .expect("There must be a `start` value.") - .value(), - "start", - "The `start` value of the text directive has the wrong value." - ); - } - } - - /// Ensures that a fragment directive that contains percent-encoded characters - /// is decoded correctly. This explicitly includes characters which are used - /// for identifying text fragments, i.e. `#`, `, `, `&`, `:`, `~` and `-`. - #[test] - fn test_parse_percent_encoding_tokens() { - let url = "#:~:text=prefix%26-,start%20and%2C,end%23,-%26suffix%2D"; - let (_, fragment_directive, text_directives) = - parse_fragment_directive_and_remove_it_from_hash(&url) - .expect("The parser must find a result."); - assert_eq!( - fragment_directive, - &url[4..], - "The extracted fragment directive string is unsanitized - and should contain the original and percent-decoded string." - ); - let text_directive = text_directives.first().unwrap(); - assert_eq!( - text_directive - .prefix() - .as_ref() - .expect("There must be a prefix.") - .value(), - "prefix&", - "" - ); - assert_eq!( - text_directive - .start() - .as_ref() - .expect("There must be a prefix.") - .value(), - "start and,", - "" - ); - assert_eq!( - text_directive - .end() - .as_ref() - .expect("There must be a prefix.") - .value(), - "end#", - "" - ); - assert_eq!( - text_directive - .suffix() - .as_ref() - .expect("There must be a prefix.") - .value(), - "&suffix-", - "" - ); - } - - /// Ensures that a text fragment is created correctly, - /// based on a given combination of tokens. - /// This includes all sorts of combinations of - /// `prefix`, `suffix`, `start` and `end`, - /// als well as values for these tokens which contain - /// characters that need to be encoded because they are - /// identifiers for text fragments - /// (#`, `, `, `&`, `:`, `~` and `-`). - #[test] - fn test_create_fragment_directive() { - for (text_directive, expected_fragment_directive) in [ - ( - TextDirective::from_parts( - String::new(), - String::from("start"), - String::new(), - String::new(), - ) - .unwrap(), - ":~:text=start", - ), - ( - TextDirective::from_parts( - String::new(), - String::from("start"), - String::from("end"), - String::new(), - ) - .unwrap(), - ":~:text=start,end", - ), - ( - TextDirective::from_parts( - String::from("prefix"), - String::from("start"), - String::from("end"), - String::new(), - ) - .unwrap(), - ":~:text=prefix-,start,end", - ), - ( - TextDirective::from_parts( - String::from("prefix"), - String::from("start"), - String::from("end"), - String::from("suffix"), - ) - .unwrap(), - ":~:text=prefix-,start,end,-suffix", - ), - ( - TextDirective::from_parts( - String::new(), - String::from("start"), - String::from("end"), - String::from("suffix"), - ) - .unwrap(), - ":~:text=start,end,-suffix", - ), - ( - TextDirective::from_parts( - String::from("prefix"), - String::from("start"), - String::new(), - String::from("suffix"), - ) - .unwrap(), - ":~:text=prefix-,start,-suffix", - ), - ( - TextDirective::from_parts( - String::from("prefix-"), - String::from("start and,"), - String::from("&end"), - String::from("#:~:suffix"), - ) - .unwrap(), - ":~:text=prefix%2D-,start%20and%2C,%26end,-%23%3A%7E%3Asuffix", - ), - ] { - let fragment_directive = create_fragment_directive_string(&vec![text_directive]) - .expect("The given input must produce a valid fragment directive."); - assert_eq!(fragment_directive, expected_fragment_directive); - } - } - - /// Ensures that a fragment directive is created correctly if multiple text fragments are given. - /// The resulting fragment must start with `:~:` - /// and each text fragment must be separated using `&text=`. - #[test] - fn test_create_fragment_directive_from_multiple_text_directives() { - let text_directives = vec![ - TextDirective::from_parts( - String::new(), - String::from("start1"), - String::new(), - String::new(), - ) - .unwrap(), - TextDirective::from_parts( - String::new(), - String::from("start2"), - String::new(), - String::new(), - ) - .unwrap(), - TextDirective::from_parts( - String::new(), - String::from("start3"), - String::new(), - String::new(), - ) - .unwrap(), - ]; - let fragment_directive = create_fragment_directive_string(&text_directives) - .expect("The given input must produce a valid fragment directive."); - assert_eq!( - fragment_directive, ":~:text=start1&text=start2&text=start3", - "The created fragment directive is wrong for multiple fragments." - ); - } -} diff --git a/dom/base/moz.build b/dom/base/moz.build index ffcfb0aaf62f..47bf4db96be3 100644 --- a/dom/base/moz.build +++ b/dom/base/moz.build @@ -194,7 +194,6 @@ EXPORTS.mozilla.dom += [ "External.h", "FilteredNodeIterator.h", "FormData.h", - "FragmentDirective.h", "FragmentOrElement.h", "FromParser.h", "GeneratedImageContent.h", @@ -298,7 +297,6 @@ if CONFIG["FUZZING"]: if CONFIG["COMPILE_ENVIRONMENT"]: EXPORTS.mozilla.dom += [ - "!fragmentdirectives_ffi_generated.h", "!GeneratedElementDocumentState.h", "RustTypes.h", ] @@ -308,11 +306,6 @@ if CONFIG["COMPILE_ENVIRONMENT"]: inputs=["rust"], ) - CbindgenHeader( - "fragmentdirectives_ffi_generated.h", - inputs=["fragmentdirectives"], - ) - UNIFIED_SOURCES += [ "!UseCounterMetrics.cpp", "AbstractRange.cpp", @@ -357,7 +350,6 @@ UNIFIED_SOURCES += [ "EventSourceEventService.cpp", "External.cpp", "FormData.cpp", - "FragmentDirective.cpp", "FragmentOrElement.cpp", "GeneratedImageContent.cpp", "GlobalTeardownObserver.cpp", diff --git a/dom/base/nsISelectionController.idl b/dom/base/nsISelectionController.idl index 9c76e45f4305..f2d5ebe65f79 100644 --- a/dom/base/nsISelectionController.idl +++ b/dom/base/nsISelectionController.idl @@ -41,12 +41,11 @@ interface nsISelectionController : nsISelectionDisplay const short SELECTION_FIND = 8; const short SELECTION_URLSECONDARY = 9; const short SELECTION_URLSTRIKEOUT = 10; - const short SELECTION_TARGET_TEXT = 11; // Custom Highlight API // (see https://drafts.csswg.org/css-highlight-api-1/#enumdef-highlighttype) - const short SELECTION_HIGHLIGHT = 12; + const short SELECTION_HIGHLIGHT = 11; // End of RawSelectionType values. - const short NUM_SELECTIONTYPES = 13; + const short NUM_SELECTIONTYPES = 12; // SelectionRegion values: const short SELECTION_ANCHOR_REGION = 0; @@ -312,7 +311,6 @@ enum class SelectionType : RawSelectionType eFind = nsISelectionController::SELECTION_FIND, eURLSecondary = nsISelectionController::SELECTION_URLSECONDARY, eURLStrikeout = nsISelectionController::SELECTION_URLSTRIKEOUT, - eTargetText = nsISelectionController::SELECTION_TARGET_TEXT, eHighlight = nsISelectionController::SELECTION_HIGHLIGHT, }; @@ -329,7 +327,6 @@ static const SelectionType kPresentSelectionTypes[] = { SelectionType::eFind, SelectionType::eURLSecondary, SelectionType::eURLStrikeout, - SelectionType::eTargetText, SelectionType::eHighlight, }; diff --git a/dom/webidl/Document.webidl b/dom/webidl/Document.webidl index 699900220b05..eb6e583320d8 100644 --- a/dom/webidl/Document.webidl +++ b/dom/webidl/Document.webidl @@ -13,7 +13,6 @@ * https://drafts.csswg.org/cssom/#extensions-to-the-document-interface * https://drafts.csswg.org/cssom-view/#extensions-to-the-document-interface * https://wicg.github.io/feature-policy/#policy - * https://wicg.github.io/scroll-to-text-fragment/#feature-detectability */ interface ContentSecurityPolicy; @@ -748,12 +747,3 @@ partial interface Document { }; Document includes NonElementParentNode; - -/** - * Extension to add the fragmentDirective property. - * https://wicg.github.io/scroll-to-text-fragment/#feature-detectability - */ -partial interface Document { - [Pref="dom.text_fragments.enabled", SameObject] - readonly attribute FragmentDirective fragmentDirective; -}; diff --git a/dom/webidl/FragmentDirective.webidl b/dom/webidl/FragmentDirective.webidl deleted file mode 100644 index 7793eaa51248..000000000000 --- a/dom/webidl/FragmentDirective.webidl +++ /dev/null @@ -1,11 +0,0 @@ -/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* 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 file, - * You can obtain one at http://mozilla.org/MPL/2.0/. - * - * The origin of this IDL file is - * https://wicg.github.io/scroll-to-text-fragment/ - */ -[Exposed=Window, Pref="dom.text_fragments.enabled"] -interface FragmentDirective { -}; diff --git a/dom/webidl/moz.build b/dom/webidl/moz.build index 1869eb3e6b4d..6ad7e124eda5 100644 --- a/dom/webidl/moz.build +++ b/dom/webidl/moz.build @@ -568,7 +568,6 @@ WEBIDL_FILES = [ "FontFaceSet.webidl", "FontFaceSource.webidl", "FormData.webidl", - "FragmentDirective.webidl", "Function.webidl", "GainNode.webidl", "Gamepad.webidl", diff --git a/layout/base/PresShell.cpp b/layout/base/PresShell.cpp index 7674abb3d07f..7e169579c62c 100644 --- a/layout/base/PresShell.cpp +++ b/layout/base/PresShell.cpp @@ -14,7 +14,6 @@ #include "mozilla/dom/AncestorIterator.h" #include "mozilla/dom/FontFaceSet.h" #include "mozilla/dom/ElementBinding.h" -#include "mozilla/dom/FragmentDirective.h" #include "mozilla/dom/LargestContentfulPaint.h" #include "mozilla/dom/MouseEventBinding.h" #include "mozilla/dom/PerformanceMainThread.h" @@ -3292,46 +3291,6 @@ 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 = - mDocument->FragmentDirective(); - - nsTArray> textDirectiveRanges = - fragmentDirective->FindTextFragmentsInDocument(); - if (textDirectiveRanges.IsEmpty()) { - return false; - } - - const RefPtr targetTextSelection = - GetCurrentSelection(SelectionType::eTargetText); - if (!targetTextSelection) { - return false; - } - for (RefPtr 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 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. * diff --git a/layout/base/PresShell.h b/layout/base/PresShell.h index 2b43b3c49270..9d274d58eaf5 100644 --- a/layout/base/PresShell.h +++ b/layout/base/PresShell.h @@ -1609,18 +1609,6 @@ 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. diff --git a/layout/generic/nsFrameSelection.cpp b/layout/generic/nsFrameSelection.cpp index ca4e3d647e9c..368b49739ed8 100644 --- a/layout/generic/nsFrameSelection.cpp +++ b/layout/generic/nsFrameSelection.cpp @@ -156,10 +156,9 @@ PeekOffsetStruct::PeekOffsetStruct(nsSelectionAmount aAmount, } // namespace mozilla -// Array which contains index of each SelectionType in -// Selection::mDOMSelections. For avoiding using if nor switch to retrieve the -// index, this needs to have -1 for SelectionTypes which won't be created its -// Selection instance. +// Array which contains index of each SelecionType in Selection::mDOMSelections. +// For avoiding using if nor switch to retrieve the index, this needs to have +// -1 for SelectionTypes which won't be created its Selection instance. static const int8_t kIndexOfSelections[] = { -1, // SelectionType::eInvalid -1, // SelectionType::eNone @@ -173,7 +172,6 @@ static const int8_t kIndexOfSelections[] = { 7, // SelectionType::eFind 8, // SelectionType::eURLSecondary 9, // SelectionType::eURLStrikeout - 10, // SelectionType::eTargetText -1, // SelectionType::eHighlight }; diff --git a/layout/generic/nsIFrame.cpp b/layout/generic/nsIFrame.cpp index c9f67158e279..213652467d3d 100644 --- a/layout/generic/nsIFrame.cpp +++ b/layout/generic/nsIFrame.cpp @@ -2402,15 +2402,6 @@ already_AddRefed nsIFrame::ComputeHighlightSelectionStyle( *element, PseudoStyleType::highlight, aHighlightName, Style()); } -already_AddRefed nsIFrame::ComputeTargetTextStyle() const { - const Element* element = FindElementAncestorForMozSelection(GetContent()); - if (!element) { - return nullptr; - } - return PresContext()->StyleSet()->ProbePseudoElementStyle( - *element, PseudoStyleType::targetText, nullptr, Style()); -} - template static inline bool IsIntrinsicKeyword(const SizeOrMaxSize& aSize) { // All keywords other than auto/none/-moz-available depend on intrinsic sizes. diff --git a/layout/generic/nsIFrame.h b/layout/generic/nsIFrame.h index 6e55ebe1c76d..90653d9927f6 100644 --- a/layout/generic/nsIFrame.h +++ b/layout/generic/nsIFrame.h @@ -937,8 +937,6 @@ class nsIFrame : public nsQueryFrame { already_AddRefed ComputeHighlightSelectionStyle( nsAtom* aHighlightName); - already_AddRefed ComputeTargetTextStyle() const; - /** * Accessor functions for geometric parent. */ diff --git a/layout/generic/nsTextFrame.cpp b/layout/generic/nsTextFrame.cpp index f6d67b526edf..ed61b93bf2d0 100644 --- a/layout/generic/nsTextFrame.cpp +++ b/layout/generic/nsTextFrame.cpp @@ -5676,10 +5676,6 @@ bool nsTextFrame::GetSelectionTextColors(SelectionType aSelectionType, aHighlightName, aBackground); return hasForeground || hasBackground; } - case SelectionType::eTargetText: { - aTextPaintStyle.GetTargetTextColors(aForeground, aBackground); - return true; - } case SelectionType::eURLSecondary: aTextPaintStyle.GetURLSecondaryColor(aForeground); *aBackground = NS_RGBA(0, 0, 0, 0); diff --git a/layout/generic/nsTextPaintStyle.cpp b/layout/generic/nsTextPaintStyle.cpp index f29fa753d0c8..e97a059b0796 100644 --- a/layout/generic/nsTextPaintStyle.cpp +++ b/layout/generic/nsTextPaintStyle.cpp @@ -213,24 +213,6 @@ void nsTextPaintStyle::GetHighlightColors(nscolor* aForeColor, *aBackColor = NS_TRANSPARENT; } -void nsTextPaintStyle::GetTargetTextColors(nscolor* aForeColor, - nscolor* aBackColor) { - NS_ASSERTION(aForeColor, "aForeColor is null"); - NS_ASSERTION(aBackColor, "aBackColor is null"); - const RefPtr targetTextStyle = - mFrame->ComputeTargetTextStyle(); - if (targetTextStyle) { - *aForeColor = targetTextStyle->GetVisitedDependentColor( - &nsStyleText::mWebkitTextFillColor); - *aBackColor = targetTextStyle->GetVisitedDependentColor( - &nsStyleBackground::mBackgroundColor); - return; - } - // XXX(:jjaschke): Before shipping this feature, a sensible set of colors must - // be set (Bug 1867940). - // in the meantime, use the colors of find selection. - GetHighlightColors(aForeColor, aBackColor); -} bool nsTextPaintStyle::GetCustomHighlightTextColor(nsAtom* aHighlightName, nscolor* aForeColor) { NS_ASSERTION(aForeColor, "aForeColor is null"); diff --git a/layout/generic/nsTextPaintStyle.h b/layout/generic/nsTextPaintStyle.h index 94cd3cf32206..adf28fdad8f7 100644 --- a/layout/generic/nsTextPaintStyle.h +++ b/layout/generic/nsTextPaintStyle.h @@ -65,7 +65,6 @@ class MOZ_STACK_CLASS nsTextPaintStyle { */ bool GetSelectionColors(nscolor* aForeColor, nscolor* aBackColor); void GetHighlightColors(nscolor* aForeColor, nscolor* aBackColor); - void GetTargetTextColors(nscolor* aForeColor, nscolor* aBackColor); // Computes colors for custom highlights. // Returns false if there are no rules associated with `aHighlightName`. bool GetCustomHighlightTextColor(nsAtom* aHighlightName, nscolor* aForeColor); diff --git a/layout/inspector/tests/test_getCSSPseudoElementNames.html b/layout/inspector/tests/test_getCSSPseudoElementNames.html index 356d4a89bc22..255f58b88917 100644 --- a/layout/inspector/tests/test_getCSSPseudoElementNames.html +++ b/layout/inspector/tests/test_getCSSPseudoElementNames.html @@ -22,7 +22,6 @@ "::highlight", "::placeholder", "::selection", - "::target-text", "::-moz-color-swatch", "::-moz-focus-inner", "::-moz-meter-bar", diff --git a/layout/style/nsCSSPseudoElementList.h b/layout/style/nsCSSPseudoElementList.h index 7492523c585a..9514dd6c4bc3 100644 --- a/layout/style/nsCSSPseudoElementList.h +++ b/layout/style/nsCSSPseudoElementList.h @@ -51,7 +51,6 @@ CSS_PSEUDO_ELEMENT(highlight, ":highlight", 0) CSS_PSEUDO_ELEMENT(selection, ":selection", CSS_PSEUDO_ELEMENT_CONTAINS_ELEMENTS) -CSS_PSEUDO_ELEMENT(targetText, ":target-text", 0) // XXXbz should we really allow random content to style these? Maybe // use our flags to prevent that? CSS_PSEUDO_ELEMENT(mozFocusInner, ":-moz-focus-inner", 0) diff --git a/layout/style/nsCSSPseudoElements.h b/layout/style/nsCSSPseudoElements.h index baeee9d4deb0..9866fbfb7eb8 100644 --- a/layout/style/nsCSSPseudoElements.h +++ b/layout/style/nsCSSPseudoElements.h @@ -130,8 +130,6 @@ class nsCSSPseudoElements { switch (aType) { case Type::highlight: return mozilla::StaticPrefs::dom_customHighlightAPI_enabled(); - case Type::targetText: - return mozilla::StaticPrefs::dom_text_fragments_enabled(); case Type::sliderTrack: case Type::sliderThumb: case Type::sliderFill: diff --git a/modules/libpref/init/StaticPrefList.yaml b/modules/libpref/init/StaticPrefList.yaml index b7aba299710a..83b21c643a7a 100644 --- a/modules/libpref/init/StaticPrefList.yaml +++ b/modules/libpref/init/StaticPrefList.yaml @@ -4135,12 +4135,6 @@ value: false mirror: always -- name: dom.text_fragments.enabled - type: RelaxedAtomicBool - value: false - mirror: always - rust: true - - name: dom.textMetrics.actualBoundingBox.enabled type: RelaxedAtomicBool value: true diff --git a/servo/components/style/gecko/pseudo_element.rs b/servo/components/style/gecko/pseudo_element.rs index f0e79a8acdb2..3bcd8734556b 100644 --- a/servo/components/style/gecko/pseudo_element.rs +++ b/servo/components/style/gecko/pseudo_element.rs @@ -159,11 +159,6 @@ impl PseudoElement { matches!(*self, Self::Highlight(_)) } - /// Whether this pseudo-element is the ::target-text pseudo. - #[inline] - pub fn is_target_text(&self) -> bool { - *self == PseudoElement::TargetText - } /// Whether this pseudo-element supports user action selectors. pub fn supports_user_action_state(&self) -> bool { (self.flags() & structs::CSS_PSEUDO_ELEMENT_SUPPORTS_USER_ACTION_STATE) != 0 @@ -173,7 +168,6 @@ impl PseudoElement { pub fn enabled_in_content(&self) -> bool { match *self { Self::Highlight(..) => pref!("dom.customHighlightAPI.enabled"), - Self::TargetText => pref!("dom.text_fragments.enabled"), Self::SliderFill | Self::SliderTrack | Self::SliderThumb => { pref!("layout.css.modern-range-pseudos.enabled") }, diff --git a/testing/web-platform/meta/css/css-pseudo/__dir__.ini b/testing/web-platform/meta/css/css-pseudo/__dir__.ini index 2ab66798ff1d..01fb2b471c74 100644 --- a/testing/web-platform/meta/css/css-pseudo/__dir__.ini +++ b/testing/web-platform/meta/css/css-pseudo/__dir__.ini @@ -1,2 +1,2 @@ -prefs: [dom.css_pseudo_element.enabled:true, layout.css.animation-composition.enabled:true, layout.css.floating-first-letter.tight-glyph-bounds:0, dom.customHighlightAPI.enabled:true, layout.css.modern-range-pseudos.enabled:true, dom.text_fragments.enabled:true] +prefs: [dom.css_pseudo_element.enabled:true, layout.css.animation-composition.enabled:true, layout.css.floating-first-letter.tight-glyph-bounds:0, dom.customHighlightAPI.enabled:true, layout.css.modern-range-pseudos.enabled:true] leak-threshold: [tab:51200] diff --git a/testing/web-platform/meta/css/css-pseudo/highlight-currentcolor-computed-visited.html.ini b/testing/web-platform/meta/css/css-pseudo/highlight-currentcolor-computed-visited.html.ini index 455a9b9fa33c..0078e751639e 100644 --- a/testing/web-platform/meta/css/css-pseudo/highlight-currentcolor-computed-visited.html.ini +++ b/testing/web-platform/meta/css/css-pseudo/highlight-currentcolor-computed-visited.html.ini @@ -1,6 +1,12 @@ [highlight-currentcolor-computed-visited.html] expected: if (os == "android") and fission: [OK, TIMEOUT] + [getComputedStyle() for ::target-text at #target1] + expected: FAIL + + [getComputedStyle() for ::target-text at #target2] + expected: FAIL + [getComputedStyle() for ::spelling-error at #target1] expected: FAIL diff --git a/testing/web-platform/meta/css/css-pseudo/highlight-currentcolor-computed.html.ini b/testing/web-platform/meta/css/css-pseudo/highlight-currentcolor-computed.html.ini index 7dc660e742bd..55fae603015d 100644 --- a/testing/web-platform/meta/css/css-pseudo/highlight-currentcolor-computed.html.ini +++ b/testing/web-platform/meta/css/css-pseudo/highlight-currentcolor-computed.html.ini @@ -1,6 +1,12 @@ [highlight-currentcolor-computed.html] expected: if (os == "android") and fission: [OK, TIMEOUT] + [getComputedStyle() for ::target-text at #target1] + expected: FAIL + + [getComputedStyle() for ::target-text at #target2] + expected: FAIL + [getComputedStyle() for ::spelling-error at #target1] expected: FAIL diff --git a/testing/web-platform/meta/css/css-pseudo/highlight-paired-cascade-004.html.ini b/testing/web-platform/meta/css/css-pseudo/highlight-paired-cascade-004.html.ini new file mode 100644 index 000000000000..896f15fbb9cf --- /dev/null +++ b/testing/web-platform/meta/css/css-pseudo/highlight-paired-cascade-004.html.ini @@ -0,0 +1,2 @@ +[highlight-paired-cascade-004.html] + expected: TIMEOUT diff --git a/testing/web-platform/meta/css/css-pseudo/highlight-pseudos-computed.html.ini b/testing/web-platform/meta/css/css-pseudo/highlight-pseudos-computed.html.ini index 65560752870e..2ba979edd042 100644 --- a/testing/web-platform/meta/css/css-pseudo/highlight-pseudos-computed.html.ini +++ b/testing/web-platform/meta/css/css-pseudo/highlight-pseudos-computed.html.ini @@ -1,4 +1,6 @@ [highlight-pseudos-computed.html] + [getComputedStyle() for ::target-text] + expected: FAIL [getComputedStyle() for ::spelling-error] expected: FAIL diff --git a/testing/web-platform/meta/css/css-pseudo/highlight-pseudos-visited-computed-001.html.ini b/testing/web-platform/meta/css/css-pseudo/highlight-pseudos-visited-computed-001.html.ini index 6e4c4d40739b..80d728c4c76c 100644 --- a/testing/web-platform/meta/css/css-pseudo/highlight-pseudos-visited-computed-001.html.ini +++ b/testing/web-platform/meta/css/css-pseudo/highlight-pseudos-visited-computed-001.html.ini @@ -1,6 +1,12 @@ [highlight-pseudos-visited-computed-001.html] expected: if (os == "android") and fission: [OK, TIMEOUT] + [getComputedStyle() for ::target-text at #target1] + expected: FAIL + + [getComputedStyle() for ::target-text at #target2] + expected: FAIL + [getComputedStyle() for ::spelling-error at #target1] expected: FAIL diff --git a/testing/web-platform/meta/css/css-pseudo/parsing/highlight-pseudos.html.ini b/testing/web-platform/meta/css/css-pseudo/parsing/highlight-pseudos.html.ini index d939b7fc470d..7cfba78b84a0 100644 --- a/testing/web-platform/meta/css/css-pseudo/parsing/highlight-pseudos.html.ini +++ b/testing/web-platform/meta/css/css-pseudo/parsing/highlight-pseudos.html.ini @@ -1,4 +1,16 @@ [highlight-pseudos.html] + ["::target-text" should be a valid selector] + expected: FAIL + + [".a::target-text" should be a valid selector] + expected: FAIL + + ["div ::target-text" should be a valid selector] + expected: FAIL + + ["::part(my-part)::target-text" should be a valid selector] + expected: FAIL + ["::spelling-error" should be a valid selector] expected: FAIL @@ -22,3 +34,4 @@ ["::part(my-part)::grammar-error" should be a valid selector] expected: FAIL + diff --git a/testing/web-platform/meta/css/css-pseudo/target-text-001.html.ini b/testing/web-platform/meta/css/css-pseudo/target-text-001.html.ini new file mode 100644 index 000000000000..d5728036e4a9 --- /dev/null +++ b/testing/web-platform/meta/css/css-pseudo/target-text-001.html.ini @@ -0,0 +1,2 @@ +[target-text-001.html] + expected: TIMEOUT diff --git a/testing/web-platform/meta/css/css-pseudo/target-text-002.html.ini b/testing/web-platform/meta/css/css-pseudo/target-text-002.html.ini new file mode 100644 index 000000000000..ab3aabf710ed --- /dev/null +++ b/testing/web-platform/meta/css/css-pseudo/target-text-002.html.ini @@ -0,0 +1,2 @@ +[target-text-002.html] + expected: TIMEOUT diff --git a/testing/web-platform/meta/css/css-pseudo/target-text-003.html.ini b/testing/web-platform/meta/css/css-pseudo/target-text-003.html.ini new file mode 100644 index 000000000000..efc8f42a77f4 --- /dev/null +++ b/testing/web-platform/meta/css/css-pseudo/target-text-003.html.ini @@ -0,0 +1,2 @@ +[target-text-003.html] + expected: TIMEOUT diff --git a/testing/web-platform/meta/css/css-pseudo/target-text-004.html.ini b/testing/web-platform/meta/css/css-pseudo/target-text-004.html.ini index 246f06888fca..3997e9109c96 100644 --- a/testing/web-platform/meta/css/css-pseudo/target-text-004.html.ini +++ b/testing/web-platform/meta/css/css-pseudo/target-text-004.html.ini @@ -1,2 +1,2 @@ [target-text-004.html] - expected: FAIL + expected: TIMEOUT diff --git a/testing/web-platform/meta/css/css-pseudo/target-text-005.html.ini b/testing/web-platform/meta/css/css-pseudo/target-text-005.html.ini new file mode 100644 index 000000000000..ba20df535ea4 --- /dev/null +++ b/testing/web-platform/meta/css/css-pseudo/target-text-005.html.ini @@ -0,0 +1,2 @@ +[target-text-005.html] + expected: TIMEOUT diff --git a/testing/web-platform/meta/css/css-pseudo/target-text-006.html.ini b/testing/web-platform/meta/css/css-pseudo/target-text-006.html.ini new file mode 100644 index 000000000000..ebb8f528fec4 --- /dev/null +++ b/testing/web-platform/meta/css/css-pseudo/target-text-006.html.ini @@ -0,0 +1,2 @@ +[target-text-006.html] + expected: TIMEOUT diff --git a/testing/web-platform/meta/css/css-pseudo/target-text-007.html.ini b/testing/web-platform/meta/css/css-pseudo/target-text-007.html.ini index a31c0ab27626..180675078fcf 100644 --- a/testing/web-platform/meta/css/css-pseudo/target-text-007.html.ini +++ b/testing/web-platform/meta/css/css-pseudo/target-text-007.html.ini @@ -1,2 +1,2 @@ [target-text-007.html] - expected: FAIL + expected: TIMEOUT diff --git a/testing/web-platform/meta/css/css-pseudo/target-text-008.html.ini b/testing/web-platform/meta/css/css-pseudo/target-text-008.html.ini index 799b233ac9f1..142c4b71fca3 100644 --- a/testing/web-platform/meta/css/css-pseudo/target-text-008.html.ini +++ b/testing/web-platform/meta/css/css-pseudo/target-text-008.html.ini @@ -1,2 +1,2 @@ [target-text-008.html] - expected: FAIL + expected: TIMEOUT diff --git a/testing/web-platform/meta/css/css-pseudo/target-text-009.html.ini b/testing/web-platform/meta/css/css-pseudo/target-text-009.html.ini index 0eadcb053641..898efdb90a3a 100644 --- a/testing/web-platform/meta/css/css-pseudo/target-text-009.html.ini +++ b/testing/web-platform/meta/css/css-pseudo/target-text-009.html.ini @@ -1,2 +1,2 @@ [target-text-009.html] - expected: FAIL + expected: TIMEOUT diff --git a/testing/web-platform/meta/css/css-pseudo/target-text-010.html.ini b/testing/web-platform/meta/css/css-pseudo/target-text-010.html.ini index f35d7539ba43..b45c19b51b60 100644 --- a/testing/web-platform/meta/css/css-pseudo/target-text-010.html.ini +++ b/testing/web-platform/meta/css/css-pseudo/target-text-010.html.ini @@ -1,2 +1,2 @@ [target-text-010.html] - expected: FAIL + expected: TIMEOUT diff --git a/testing/web-platform/meta/css/css-pseudo/target-text-dynamic-001.html.ini b/testing/web-platform/meta/css/css-pseudo/target-text-dynamic-001.html.ini new file mode 100644 index 000000000000..afa4d7e84353 --- /dev/null +++ b/testing/web-platform/meta/css/css-pseudo/target-text-dynamic-001.html.ini @@ -0,0 +1,2 @@ +[target-text-dynamic-001.html] + expected: TIMEOUT diff --git a/testing/web-platform/meta/css/css-pseudo/target-text-dynamic-002.html.ini b/testing/web-platform/meta/css/css-pseudo/target-text-dynamic-002.html.ini index 7ab9a36a644e..ce592543c9b9 100644 --- a/testing/web-platform/meta/css/css-pseudo/target-text-dynamic-002.html.ini +++ b/testing/web-platform/meta/css/css-pseudo/target-text-dynamic-002.html.ini @@ -1,2 +1,2 @@ [target-text-dynamic-002.html] - expected: FAIL + expected: TIMEOUT diff --git a/testing/web-platform/meta/css/css-pseudo/target-text-dynamic-003.html.ini b/testing/web-platform/meta/css/css-pseudo/target-text-dynamic-003.html.ini new file mode 100644 index 000000000000..5e34d13cfeb5 --- /dev/null +++ b/testing/web-platform/meta/css/css-pseudo/target-text-dynamic-003.html.ini @@ -0,0 +1,2 @@ +[target-text-dynamic-003.html] + expected: TIMEOUT diff --git a/testing/web-platform/meta/css/css-pseudo/target-text-dynamic-004.html.ini b/testing/web-platform/meta/css/css-pseudo/target-text-dynamic-004.html.ini index c8b5a4ab11b7..65461fc3e801 100644 --- a/testing/web-platform/meta/css/css-pseudo/target-text-dynamic-004.html.ini +++ b/testing/web-platform/meta/css/css-pseudo/target-text-dynamic-004.html.ini @@ -1,2 +1,2 @@ [target-text-dynamic-004.html] - expected: FAIL + expected: TIMEOUT diff --git a/testing/web-platform/meta/css/css-pseudo/target-text-text-decoration-001.html.ini b/testing/web-platform/meta/css/css-pseudo/target-text-text-decoration-001.html.ini index a73d8c891ca7..326dc0329ca3 100644 --- a/testing/web-platform/meta/css/css-pseudo/target-text-text-decoration-001.html.ini +++ b/testing/web-platform/meta/css/css-pseudo/target-text-text-decoration-001.html.ini @@ -1,2 +1,2 @@ [target-text-text-decoration-001.html] - expected: FAIL + expected: TIMEOUT diff --git a/testing/web-platform/meta/scroll-to-text-fragment/__dir__.ini b/testing/web-platform/meta/scroll-to-text-fragment/__dir__.ini index 99241de03578..19f5f83c8904 100644 --- a/testing/web-platform/meta/scroll-to-text-fragment/__dir__.ini +++ b/testing/web-platform/meta/scroll-to-text-fragment/__dir__.ini @@ -1,2 +1,2 @@ # https://bugzilla.mozilla.org/show_bug.cgi?id=1753933 -prefs: [dom.text_fragments.enabled:true] +implementation-status: backlog diff --git a/testing/web-platform/meta/scroll-to-text-fragment/find-range-from-text-directive.html.ini b/testing/web-platform/meta/scroll-to-text-fragment/find-range-from-text-directive.html.ini index 7ee986303605..472509590550 100644 --- a/testing/web-platform/meta/scroll-to-text-fragment/find-range-from-text-directive.html.ini +++ b/testing/web-platform/meta/scroll-to-text-fragment/find-range-from-text-directive.html.ini @@ -1,9 +1,207 @@ [find-range-from-text-directive.html] - [Search invisible content between prefix and match.] - expected: FAIL + expected: + if os == "mac": [OK, TIMEOUT] + [TIMEOUT, OK] + [inverted range search.] + expected: + if os == "mac": [PASS, TIMEOUT, NOTRUN] + if os == "linux": [PASS, NOTRUN, TIMEOUT] + if os == "win": [PASS, TIMEOUT, NOTRUN] + [NOTRUN, PASS, TIMEOUT] - [Search invisible content between |end| and suffix.] + [Suffix comes before |end|.] + expected: + if os == "win": [PASS, NOTRUN, TIMEOUT] + if os == "mac": [PASS, TIMEOUT, NOTRUN] + [NOTRUN, PASS, TIMEOUT] + + [Multiple overlapping prefixes.] expected: FAIL [match doesn't immediately follow prefix.] + expected: + if os == "android": [NOTRUN, PASS, TIMEOUT] + FAIL + + [match doesn't immediately follow first prefix instance.] + expected: + if os == "android": [FAIL, TIMEOUT, NOTRUN, PASS] + [FAIL, PASS, TIMEOUT, NOTRUN] + + [Suffix must be end bounded.] + expected: + if os == "win": [PASS, NOTRUN, TIMEOUT] + if os == "mac": [PASS, TIMEOUT, NOTRUN] + [NOTRUN, PASS, TIMEOUT] + + [non-existent exact match.] + expected: + if os == "android": [NOTRUN, PASS, TIMEOUT] + [PASS, TIMEOUT, NOTRUN] + + [non-existent range match.] + expected: + if os == "android": [NOTRUN, PASS, TIMEOUT] + [PASS, TIMEOUT, NOTRUN] + + [Multiple overlapping one letter prefixes.] expected: FAIL + + [Content appears between match and suffix.] + expected: + if os == "win": [PASS, NOTRUN, TIMEOUT] + if os == "mac": [PASS, TIMEOUT, NOTRUN] + [NOTRUN, PASS] + + [overlapping exact matches with suffix.] + expected: + if os == "android": [NOTRUN, FAIL, TIMEOUT] + [FAIL, TIMEOUT, NOTRUN] + + [Match with no suffix.] + expected: + if os == "mac": [FAIL, TIMEOUT, NOTRUN] + if os == "win": [FAIL, NOTRUN, TIMEOUT] + [NOTRUN, FAIL, TIMEOUT] + + [Match text after prefix.] + expected: FAIL + + [Search invisible content between |end| and suffix.] + expected: + if os == "win": [FAIL, TIMEOUT, NOTRUN] + if os == "mac": [FAIL, TIMEOUT, NOTRUN] + [NOTRUN, FAIL, TIMEOUT] + + [Range with preceeding suffix.] + expected: + if os == "linux": [NOTRUN, TIMEOUT, PASS] + if os == "win": [PASS, TIMEOUT, NOTRUN] + if os == "mac": [PASS, TIMEOUT, NOTRUN] + [NOTRUN, PASS, TIMEOUT] + + [no suffix forces |end| to be end bounded.] + expected: + if os == "android": [NOTRUN, PASS, TIMEOUT] + [PASS, TIMEOUT, NOTRUN] + + [|end| must be start bounded even if full range is word bounded.] + expected: + if os == "linux": [PASS, NOTRUN, TIMEOUT] + if os == "win": [PASS, TIMEOUT, NOTRUN] + if os == "mac": [PASS, TIMEOUT, NOTRUN] + [NOTRUN, PASS, TIMEOUT] + + [non-existent |end|.] + expected: + if os == "win": [PASS, TIMEOUT, NOTRUN] + if os == "mac": [PASS, TIMEOUT, NOTRUN] + [NOTRUN, PASS, TIMEOUT] + + [suffix means |start| need not end on word boundary.] + expected: + if os == "linux": [FAIL, NOTRUN] + FAIL + + [Search invisible content between prefix and match.] + expected: FAIL + + [overlapping one letter exact matches with suffix.] + expected: + if os == "android": [NOTRUN, FAIL, TIMEOUT] + [FAIL, TIMEOUT, NOTRUN] + + [Basic smoke test - full word match.] + expected: FAIL + + [matching range search.] + expected: + if os == "android": [NOTRUN, FAIL, TIMEOUT] + [FAIL, TIMEOUT, NOTRUN] + + [Prefix need not end on a word boundary.] + expected: FAIL + + [|start| doesn't need to start on word boundary.] + expected: + if os == "linux": [FAIL, NOTRUN] + FAIL + + [no-prefix; suffix means |start| need not end on word boundary.] + expected: + if os == "android": [NOTRUN, FAIL, TIMEOUT] + [FAIL, TIMEOUT, NOTRUN] + + [no-prefix; |end| forces |start| to end on word boundary.] + expected: + if os == "android": [NOTRUN, TIMEOUT, PASS] + [PASS, TIMEOUT, NOTRUN] + + [no-prefix; no |end| or suffix forces |start| to end on word boundary.] + expected: + if os == "android": [NOTRUN, PASS, TIMEOUT] + [PASS, TIMEOUT, NOTRUN] + + [Non-existent suffix.] + expected: + if os == "win": [PASS, NOTRUN, TIMEOUT] + if os == "mac": [PASS, TIMEOUT, NOTRUN] + [NOTRUN, PASS, TIMEOUT] + + [Suffix need not be start bounded.] + expected: + if os == "win": [FAIL, NOTRUN, TIMEOUT] + if os == "mac": [FAIL, TIMEOUT, NOTRUN] + [NOTRUN, FAIL, TIMEOUT] + + [|end| must be start bounded.] + expected: + if os == "mac": [PASS, TIMEOUT, NOTRUN] + if os == "linux": [PASS, NOTRUN, TIMEOUT] + if os == "win": [PASS, TIMEOUT, NOTRUN] + [NOTRUN, PASS, TIMEOUT] + + [suffix means |end| need not be end bounded.] + expected: + if os == "win": [FAIL, TIMEOUT, NOTRUN] + if os == "mac": [FAIL, TIMEOUT, NOTRUN] + if os == "linux": [FAIL, NOTRUN, TIMEOUT] + [NOTRUN, FAIL, TIMEOUT] + + [|start| must start on a word boundary.] + expected: + if os == "android": [NOTRUN, PASS, TIMEOUT] + [PASS, TIMEOUT, NOTRUN] + + [Non-matching suffix search continues to prefix match.] + expected: + if os == "mac": [FAIL, NOTRUN, TIMEOUT] + if os == "win": [FAIL, NOTRUN, TIMEOUT] + [NOTRUN, FAIL, TIMEOUT] + + [Range end matches correct suffix.] + expected: + if os == "mac": [FAIL, NOTRUN, TIMEOUT] + [NOTRUN, FAIL, TIMEOUT] + + [Non-matching suffix in first potential match.] + expected: + if os == "mac": [FAIL, TIMEOUT, NOTRUN] + if os == "win": [FAIL, NOTRUN, TIMEOUT] + [NOTRUN, FAIL, TIMEOUT] + + [match doesn't immediately follow prefix.] + expected: [PASS, TIMEOUT, NOTRUN] + + [prefix with non-existent range match.] + expected: + if os == "linux": [PASS, TIMEOUT, NOTRUN] + [PASS, TIMEOUT] + + [no |end| or suffix forces |start| to end on word boundary.] + expected: + if os == "linux": [PASS, TIMEOUT] + + [prefix with non-existent exact match.] + expected: + if os == "linux": [PASS, NOTRUN] diff --git a/testing/web-platform/meta/scroll-to-text-fragment/force-load-at-top.html.ini b/testing/web-platform/meta/scroll-to-text-fragment/force-load-at-top.html.ini index dd29e2295c9e..8c76e0b820f0 100644 --- a/testing/web-platform/meta/scroll-to-text-fragment/force-load-at-top.html.ini +++ b/testing/web-platform/meta/scroll-to-text-fragment/force-load-at-top.html.ini @@ -1,4 +1,7 @@ [force-load-at-top.html] + expected: + if os == "linux": OK + [OK, TIMEOUT] [force-load-at-top must block scroll on load from element fragment.] expected: [FAIL, PASS, TIMEOUT, NOTRUN] @@ -7,6 +10,12 @@ if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] + [no-force-load-at-top must not block scroll on load from text fragment.] + expected: [FAIL, TIMEOUT, NOTRUN] + + [no-force-load-at-top must not block scroll on load from text fragment with element fallback.] + expected: [FAIL, TIMEOUT, NOTRUN] + [no-force-load-at-top must not block scroll on load from element fragment.] expected: [PASS, TIMEOUT, NOTRUN] @@ -15,7 +24,4 @@ [force-load-at-top must block scroll on load from text fragment with element fallback.] expected: - FAIL - - [force-load-at-top must block scroll on load from text fragment.] - expected: FAIL + if os == "win": [PASS, TIMEOUT] diff --git a/testing/web-platform/meta/scroll-to-text-fragment/idlharness.window.js.ini b/testing/web-platform/meta/scroll-to-text-fragment/idlharness.window.js.ini new file mode 100644 index 000000000000..4585cd7766a3 --- /dev/null +++ b/testing/web-platform/meta/scroll-to-text-fragment/idlharness.window.js.ini @@ -0,0 +1,32 @@ +[idlharness.window.html] + expected: + if os == "android": [OK, TIMEOUT] + [FragmentDirective interface: existence and properties of interface object] + expected: FAIL + + [FragmentDirective interface object length] + expected: FAIL + + [FragmentDirective interface object name] + expected: FAIL + + [FragmentDirective interface: existence and properties of interface prototype object] + expected: FAIL + + [FragmentDirective interface: existence and properties of interface prototype object's "constructor" property] + expected: FAIL + + [FragmentDirective interface: existence and properties of interface prototype object's @@unscopables property] + expected: FAIL + + [Stringification of document.fragmentDirective] + expected: FAIL + + [Document interface: document must inherit property "fragmentDirective" with the proper type] + expected: FAIL + + [FragmentDirective must be primary interface of document.fragmentDirective] + expected: FAIL + + [Document interface: attribute fragmentDirective] + expected: FAIL diff --git a/testing/web-platform/meta/scroll-to-text-fragment/iframe-scroll.sub.html.ini b/testing/web-platform/meta/scroll-to-text-fragment/iframe-scroll.sub.html.ini index 84b0f398c3ed..4515c94d9576 100644 --- a/testing/web-platform/meta/scroll-to-text-fragment/iframe-scroll.sub.html.ini +++ b/testing/web-platform/meta/scroll-to-text-fragment/iframe-scroll.sub.html.ini @@ -1,3 +1,7 @@ [iframe-scroll.sub.html] + expected: TIMEOUT + [CROSS-ORIGIN: Text directive in iframe doesn't bubble to outer frame.] + expected: TIMEOUT + [SAME-ORIGIN: Text directive in iframe bubbles to outer frame.] - expected: FAIL + expected: NOTRUN diff --git a/testing/web-platform/meta/scroll-to-text-fragment/iframes.sub.html.ini b/testing/web-platform/meta/scroll-to-text-fragment/iframes.sub.html.ini index 4709d3dee85e..b2d3eac0a534 100644 --- a/testing/web-platform/meta/scroll-to-text-fragment/iframes.sub.html.ini +++ b/testing/web-platform/meta/scroll-to-text-fragment/iframes.sub.html.ini @@ -5,6 +5,9 @@ [Navigate same-origin iframe via window.location] expected: FAIL + [Navigate cross-origin iframe via window.location] + expected: FAIL + [Cross-origin with element-id fallback] expected: FAIL diff --git a/testing/web-platform/meta/scroll-to-text-fragment/non-html-documents.html.ini b/testing/web-platform/meta/scroll-to-text-fragment/non-html-documents.html.ini index 6e18e0dda00f..a984668b1dc0 100644 --- a/testing/web-platform/meta/scroll-to-text-fragment/non-html-documents.html.ini +++ b/testing/web-platform/meta/scroll-to-text-fragment/non-html-documents.html.ini @@ -1,13 +1,21 @@ [non-html-documents.html] - expected: TIMEOUT - [Text directive blocked in text/javascript] + expected: + if os == "android": [TIMEOUT, OK] + TIMEOUT + [Text directive allowed in text/plain] + expected: + if os == "android": [FAIL, TIMEOUT] + NOTRUN + + [Text directive blocked in application/xml] + expected: + if os == "android": [TIMEOUT, NOTRUN] + NOTRUN + + [Text directive allowed in text/html] expected: FAIL [Text directive blocked in application/json] - expected: [TIMEOUT, FAIL, NOTRUN] - - [Text directive allowed in text/plain] - expected: [NOTRUN, FAIL, TIMEOUT, PASS] - - [Text directive blocked in application/xml] - expected: [NOTRUN, FAIL, TIMEOUT] + expected: + if os == "android": PASS + TIMEOUT diff --git a/testing/web-platform/meta/scroll-to-text-fragment/percent-encoding.html.ini b/testing/web-platform/meta/scroll-to-text-fragment/percent-encoding.html.ini index 177ffdfe0227..3b19f7530b69 100644 --- a/testing/web-platform/meta/scroll-to-text-fragment/percent-encoding.html.ini +++ b/testing/web-platform/meta/scroll-to-text-fragment/percent-encoding.html.ini @@ -1,15 +1,31 @@ [percent-encoding.html] - [Test navigation with fragment: Percent-encoding limited to two digits..] + expected: + if os == "win": [OK, TIMEOUT] + [Test navigation with fragment: Percent-encoded "%" char..] expected: FAIL + [Test navigation with fragment: Single digit percent-encoding is invalid..] + expected: + if os == "win": [PASS, TIMEOUT] + if os == "android": [PASS, TIMEOUT, NOTRUN] + + [Test navigation with fragment: Percent-encoding limited to two digits..] + expected: + if os == "win": [FAIL, NOTRUN] + FAIL + [Test navigation with fragment: Percent-encoded "%%F".] - expected: FAIL + expected: + if os == "win": [FAIL, TIMEOUT, NOTRUN] + if os == "linux": [FAIL, TIMEOUT, NOTRUN] + FAIL [Test navigation with fragment: Percent-encoding multibyte codepoint (CHECKMARK)..] - expected: FAIL - - [Test navigation with fragment: Percent char without hex digits is invalid..] - expected: [FAIL, PASS] + expected: + if os == "win": [FAIL, TIMEOUT, NOTRUN] + if os == "linux": [FAIL, TIMEOUT, NOTRUN] + FAIL [Test navigation with fragment: Percent char followed by percent char is invalid..] - expected: [FAIL, PASS] + expected: + if os == "android": [PASS, TIMEOUT] diff --git a/testing/web-platform/meta/scroll-to-text-fragment/same-document-tests.html.ini b/testing/web-platform/meta/scroll-to-text-fragment/same-document-tests.html.ini index c277558b1b6c..8a0a413e2ce2 100644 --- a/testing/web-platform/meta/scroll-to-text-fragment/same-document-tests.html.ini +++ b/testing/web-platform/meta/scroll-to-text-fragment/same-document-tests.html.ini @@ -1,8 +1,11 @@ [same-document-tests.html] expected: - [OK, TIMEOUT] + if os == "android": [OK, TIMEOUT] + [Basic text directive navigation] + expected: FAIL + [Basic element id fallback] expected: FAIL [Malformed text directive element id fallback] - expected: [FAIL, TIMEOUT] + expected: FAIL diff --git a/testing/web-platform/meta/scroll-to-text-fragment/scroll-to-text-fragment-api.html.ini b/testing/web-platform/meta/scroll-to-text-fragment/scroll-to-text-fragment-api.html.ini index cbb55372cea4..f1a3bca9e4ec 100644 --- a/testing/web-platform/meta/scroll-to-text-fragment/scroll-to-text-fragment-api.html.ini +++ b/testing/web-platform/meta/scroll-to-text-fragment/scroll-to-text-fragment-api.html.ini @@ -1,3 +1,9 @@ [scroll-to-text-fragment-api.html] disabled: if (os == "win") and (processor == "x86_64"): random test + [Scroll to text is feature detectable via document.fragmentDirective] + expected: FAIL + + [Setting document.fragmentDirective has no effect] + expected: FAIL + diff --git a/testing/web-platform/meta/scroll-to-text-fragment/scroll-to-text-fragment-same-doc.html.ini b/testing/web-platform/meta/scroll-to-text-fragment/scroll-to-text-fragment-same-doc.html.ini index a2c6bcb3aaeb..0639b76c8c11 100644 --- a/testing/web-platform/meta/scroll-to-text-fragment/scroll-to-text-fragment-same-doc.html.ini +++ b/testing/web-platform/meta/scroll-to-text-fragment/scroll-to-text-fragment-same-doc.html.ini @@ -1,3 +1,7 @@ [scroll-to-text-fragment-same-doc.html] + [Activated for same-document window.location setter] + expected: FAIL + [Activated for same-document window.location.replace] expected: FAIL + diff --git a/testing/web-platform/meta/scroll-to-text-fragment/scroll-to-text-fragment-security.sub.html.ini b/testing/web-platform/meta/scroll-to-text-fragment/scroll-to-text-fragment-security.sub.html.ini index 1cfd578df90e..d170a7e55f2f 100644 --- a/testing/web-platform/meta/scroll-to-text-fragment/scroll-to-text-fragment-security.sub.html.ini +++ b/testing/web-platform/meta/scroll-to-text-fragment/scroll-to-text-fragment-security.sub.html.ini @@ -1,17 +1,24 @@ [scroll-to-text-fragment-security.sub.html] - expected: [OK, TIMEOUT] + expected: + if os == "android": [TIMEOUT, OK] + [OK, TIMEOUT] + [Test that a text fragment directive requires a user activation (user_activation=true).] + expected: [FAIL, TIMEOUT, NOTRUN] - [Test that a text fragment directive is not activated within an iframe.] - expected: FAIL - - [Test that a text fragment directive is not activated when there is a window opener (noopener=false).] - expected: FAIL - [Test that a text fragment directive is not activated when there is a window opener (noopener=true).] - expected: [FAIL, PASS] + expected: [FAIL, TIMEOUT, NOTRUN] [Test that a text fragment directive requires a user activation (user_activation=false).] - expected: FAIL + expected: [FAIL, TIMEOUT, NOTRUN] - [Test that a text fragment directive requires a user activation (user_activation=true).] - expected: [FAIL, PASS] + [Test that a text fragment directive is not activated within an iframe.] + expected: + if os == "linux": [FAIL, NOTRUN, TIMEOUT] + if os == "android": [NOTRUN, FAIL, TIMEOUT] + [FAIL, TIMEOUT, NOTRUN] + + [Test that a text fragment directive is not activated when there is a window opener (noopener=false).] + expected: + if os == "linux": [FAIL, NOTRUN, TIMEOUT] + if os == "android": [TIMEOUT, FAIL, NOTRUN] + [FAIL, TIMEOUT, NOTRUN] diff --git a/testing/web-platform/meta/scroll-to-text-fragment/scroll-to-text-fragment.html.ini b/testing/web-platform/meta/scroll-to-text-fragment/scroll-to-text-fragment.html.ini index 4fef7aca0378..1dc328c049a7 100644 --- a/testing/web-platform/meta/scroll-to-text-fragment/scroll-to-text-fragment.html.ini +++ b/testing/web-platform/meta/scroll-to-text-fragment/scroll-to-text-fragment.html.ini @@ -1,88 +1,275 @@ [scroll-to-text-fragment.html] - [Test navigation with fragment: Text directive with invalid syntax (context terms without "-") should not parse as a text directive.] - expected: 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 - + expected: + if os == "mac": [OK, TIMEOUT] + if os == "win": [OK, TIMEOUT] + [TIMEOUT, OK] [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 + expected: [FAIL, TIMEOUT, NOTRUN] [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: FAIL + expected: [FAIL, TIMEOUT, NOTRUN] [Test navigation with fragment: Generic fragment directive with existing element fragment should scroll to element.] - expected: FAIL + expected: [FAIL, TIMEOUT, NOTRUN] + + [Test navigation with fragment: Exact text with prefix should match text.] + expected: [FAIL, TIMEOUT, NOTRUN] + + [Test navigation with fragment: Non-matching text directive with existing element fragment should scroll to element.] + expected: [FAIL, TIMEOUT, NOTRUN] + + [Test navigation with fragment: Exact text with suffix should match text.] + expected: [FAIL, TIMEOUT, NOTRUN] + + [Test navigation with fragment: Text directive should match when context terms are separated by node boundaries.] + expected: [FAIL, TIMEOUT, NOTRUN] + + [Test navigation with fragment: Non-matching text directive with nonexistent element fragment should not match and not scroll.] + expected: [FAIL, TIMEOUT, NOTRUN] + + [Test navigation with fragment: Non-whole-word exact text with spaces should not match.] + expected: [FAIL, TIMEOUT, NOTRUN] + + [Test navigation with fragment: Text range with prefix and suffix should match text.] + expected: [FAIL, TIMEOUT, NOTRUN] + + [Test navigation with fragment: Text directive should not scroll to hidden text.] + expected: [FAIL, TIMEOUT, NOTRUN] + + [Test navigation with fragment: Fragment directive with percent encoded non-ASCII unicode character should match text.] + expected: [FAIL, TIMEOUT, NOTRUN] + + [Test navigation with fragment: Multiple match text directive disambiguated by prefix should match the prefixed text.] + expected: [FAIL, TIMEOUT, NOTRUN] + + [Test navigation with fragment: Text range with no context should match text.] + expected: [FAIL, TIMEOUT, NOTRUN] + + [Test navigation with fragment: Uppercase TEXT directive should not parse as a text directive.] + expected: [FAIL, TIMEOUT, NOTRUN] + + [Test navigation with fragment: Text range with prefix and nonmatching suffix should not match.] + expected: [FAIL, TIMEOUT, NOTRUN] + + [Test navigation with fragment: Multiple non-whole-word exact texts should not match.] + expected: [FAIL, TIMEOUT, NOTRUN] + + [Test navigation with fragment: Text directive should match text within shadow DOM.] + expected: [FAIL, TIMEOUT, NOTRUN] + + [Test navigation with fragment: Text range with non-matching startText should not match.] + expected: [FAIL, TIMEOUT, NOTRUN] + + [Test navigation with fragment: Text directive with invalid syntax (context terms without "-") should not parse as a text directive.] + expected: [FAIL, TIMEOUT, NOTRUN] + + [Test navigation with fragment: Multiple match text directive disambiguated by prefix and suffix should match the text with the given context.] + expected: [FAIL, TIMEOUT, NOTRUN] + + [Test navigation with fragment: Text directive with existing element fragment should match and scroll into view text.] + expected: [FAIL, TIMEOUT, NOTRUN] + + [Test navigation with fragment: Text range with prefix should match text.] + expected: [FAIL, TIMEOUT, NOTRUN] + + [Test navigation with fragment: Text range with suffix should match text.] + expected: [FAIL, TIMEOUT, NOTRUN] + + [Test navigation with fragment: Text directive with nonexistent element fragment should match and scroll into view text.] + expected: [FAIL, TIMEOUT, NOTRUN] + + [Test navigation with fragment: Text range with nonmatching prefix and matching suffix should not match.] + expected: [FAIL, TIMEOUT, NOTRUN] + + [Test navigation with fragment: Exact text with prefix and suffix should match text.] + expected: [FAIL, TIMEOUT, NOTRUN] + + [Test navigation with fragment: Fragment directive with all TextMatchChars should match text.] + expected: [FAIL, TIMEOUT, NOTRUN] + + [Test navigation with fragment: Exact text with percent encoded spaces should match text.] + expected: [FAIL, TIMEOUT, NOTRUN] + + [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, TIMEOUT, NOTRUN] + + [Test navigation with fragment: Text directive should not scroll to display none text.] + expected: [FAIL, TIMEOUT, NOTRUN] + + [Test navigation with fragment: Multiple text directives and a non-text directive should match text.] + expected: [FAIL, TIMEOUT, NOTRUN] + + [Test navigation with fragment: Multiple matching exact texts should match text.] + expected: [FAIL, TIMEOUT, NOTRUN] + + [Test navigation with fragment: Text range with non-matching endText should not match.] + expected: [FAIL, TIMEOUT, NOTRUN] + + [Test navigation with fragment: Exact text with no context should match text.] + expected: [FAIL, TIMEOUT, NOTRUN] + + [Test navigation with fragment: Text directive should horizontally scroll into view.] + expected: [FAIL, TIMEOUT, NOTRUN] + + [Test navigation with fragment: Text directive followed by non-text directive should match text.] + expected: [FAIL, TIMEOUT, NOTRUN] + + [Test navigation with fragment: Fragment directive with percent encoded syntactical characters "&,-" should match text.] + expected: + if os == "android": [TIMEOUT, NOTRUN, FAIL] + [FAIL, TIMEOUT, NOTRUN] [Test navigation with fragment: Non-matching text directive with existing element fragment should scroll to element.] expected: - if os == "android": [PASS, NOTRUN, FAIL, TIMEOUT] \ No newline at end of file + if os == "android": [NOTRUN, FAIL, TIMEOUT] + [FAIL, TIMEOUT, NOTRUN] + + [Test navigation with fragment: Generic fragment directive with existing element fragment should scroll to element.] + expected: [FAIL, TIMEOUT, NOTRUN] + + [Test navigation with fragment: Exact text with prefix should match text.] + expected: [FAIL, TIMEOUT, NOTRUN] + + [Test navigation with fragment: Multiple match text directive disambiguated by suffix should match the suffixed text.] + expected: + if os == "android": [NOTRUN, FAIL, TIMEOUT] + [FAIL, TIMEOUT, NOTRUN] + + [Test navigation with fragment: Exact text with suffix should match text.] + expected: [FAIL, TIMEOUT, NOTRUN] + + [Test navigation with fragment: Text directive should match when context terms are separated by node boundaries.] + expected: + if os == "win": [FAIL, TIMEOUT, NOTRUN] + if os == "mac": [FAIL, TIMEOUT, NOTRUN] + [NOTRUN, FAIL, TIMEOUT] + + [Test navigation with fragment: Non-matching text directive with nonexistent element fragment should not match and not scroll.] + expected: + if os == "android": [NOTRUN, FAIL, TIMEOUT] + [FAIL, TIMEOUT, NOTRUN] + + [Test navigation with fragment: Non-whole-word exact text with spaces should not match.] + expected: [FAIL, TIMEOUT, NOTRUN] + + [Test navigation with fragment: Text range with prefix and suffix should match text.] + expected: [FAIL, TIMEOUT, NOTRUN] + + [Test navigation with fragment: Text directive should not scroll to hidden text.] + expected: + if os == "mac": [FAIL, TIMEOUT, NOTRUN] + if os == "win": [FAIL, TIMEOUT, NOTRUN] + [NOTRUN, FAIL, TIMEOUT] + + [Test navigation with fragment: Multiple match text directive disambiguated by prefix should match the prefixed text.] + expected: + if os == "android": [NOTRUN, FAIL, TIMEOUT] + [FAIL, TIMEOUT, NOTRUN] + + [Test navigation with fragment: Text range with no context should match text.] + expected: [FAIL, TIMEOUT, NOTRUN] + + [Test navigation with fragment: Text range with prefix and nonmatching suffix should not match.] + expected: [FAIL, TIMEOUT, NOTRUN] + + [Test navigation with fragment: Multiple non-whole-word exact texts should not match.] + expected: + if os == "android": [NOTRUN, FAIL, TIMEOUT] + [FAIL, TIMEOUT, NOTRUN] + + [Test navigation with fragment: Multiple text directives and a non-text directive should match text.] + expected: + if os == "android": [NOTRUN, FAIL, TIMEOUT] + [FAIL, TIMEOUT, NOTRUN] + + [Test navigation with fragment: Text directive should match text within shadow DOM.] + expected: + if os == "win": [FAIL, TIMEOUT, NOTRUN] + if os == "mac": [FAIL, TIMEOUT, NOTRUN] + [NOTRUN, FAIL, TIMEOUT] + + [Test navigation with fragment: Text range with non-matching startText should not match.] + expected: [FAIL, TIMEOUT, NOTRUN] + + [Test navigation with fragment: Text directive with invalid syntax (context terms without "-") should not parse as a text directive.] + expected: [FAIL, TIMEOUT, NOTRUN] + + [Test navigation with fragment: Text directive with existing element fragment should match and scroll into view text.] + expected: + if os == "android": [NOTRUN, FAIL, TIMEOUT] + [FAIL, TIMEOUT, NOTRUN] + + [Test navigation with fragment: Multiple match text directive disambiguated by prefix and suffix should match the text with the given context.] + expected: + if os == "linux": [TIMEOUT, FAIL, NOTRUN] + if os == "android": [NOTRUN, FAIL, TIMEOUT] + [FAIL, TIMEOUT, NOTRUN] + + [Test navigation with fragment: Text range with non-matching endText should not match.] + expected: [FAIL, TIMEOUT, NOTRUN] + + [Test navigation with fragment: Text range with prefix should match text.] + expected: [FAIL, TIMEOUT, NOTRUN] + + [Test navigation with fragment: Text range with suffix should match text.] + expected: [FAIL, TIMEOUT, NOTRUN] + + [Test navigation with fragment: Text directive with nonexistent element fragment should match and scroll into view text.] + expected: + if os == "android": [NOTRUN, FAIL, TIMEOUT] + [FAIL, TIMEOUT, NOTRUN] + + [Test navigation with fragment: Text range with nonmatching prefix and matching suffix should not match.] + expected: [FAIL, TIMEOUT, NOTRUN] + + [Test navigation with fragment: Exact text with prefix and suffix should match text.] + expected: [FAIL, TIMEOUT, NOTRUN] + + [Test navigation with fragment: Fragment directive with all TextMatchChars should match text.] + expected: + if os == "android": [NOTRUN, FAIL, TIMEOUT] + [FAIL, TIMEOUT, NOTRUN] + + [Test navigation with fragment: Exact text with percent encoded spaces should match text.] + expected: [FAIL, TIMEOUT, NOTRUN] + + [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: + if os == "android": [NOTRUN, FAIL, TIMEOUT] + [FAIL, TIMEOUT, NOTRUN] + + [Test navigation with fragment: Text directive should not scroll to display none text.] + expected: + if os == "win": [FAIL, TIMEOUT, NOTRUN] + if os == "mac": [FAIL, TIMEOUT, NOTRUN] + [NOTRUN, FAIL, TIMEOUT] + + [Test navigation with fragment: Uppercase TEXT directive should not parse as a text directive.] + expected: [FAIL, TIMEOUT, NOTRUN] + + [Test navigation with fragment: Multiple matching exact texts should match text.] + expected: + if os == "android": [NOTRUN, FAIL, TIMEOUT] + [FAIL, TIMEOUT, NOTRUN] + + [Test navigation with fragment: Fragment directive with percent encoded non-ASCII unicode character should match text.] + expected: + if os == "android": [NOTRUN, FAIL, TIMEOUT] + [FAIL, TIMEOUT, NOTRUN] + + [Test navigation with fragment: Exact text with no context should match text.] + expected: [FAIL, TIMEOUT, NOTRUN] + + [Test navigation with fragment: Text directive should horizontally scroll into view.] + expected: + if os == "win": [FAIL, TIMEOUT, NOTRUN] + if os == "mac": [FAIL, TIMEOUT, NOTRUN] + [NOTRUN, FAIL, TIMEOUT] + + [Test navigation with fragment: Text directive followed by non-text directive should match text.] + expected: + if os == "android": [NOTRUN, FAIL, TIMEOUT] + [FAIL, TIMEOUT, NOTRUN] + + [Test navigation with fragment: Exact text with prefix and suffix and query equals prefix..] + expected: [FAIL, TIMEOUT, NOTRUN] diff --git a/toolkit/library/rust/shared/Cargo.toml b/toolkit/library/rust/shared/Cargo.toml index a542f707a144..bc7ddeb33751 100644 --- a/toolkit/library/rust/shared/Cargo.toml +++ b/toolkit/library/rust/shared/Cargo.toml @@ -67,7 +67,6 @@ mozannotation_server = { path = "../../../crashreporter/mozannotation_server", gecko-profiler = { path = "../../../../tools/profiler/rust-api"} midir_impl = { path = "../../../../dom/midi/midir_impl", optional = true } dom = { path = "../../../../dom/base/rust" } -dom_fragmentdirectives = { path="../../../../dom/base/fragmentdirectives" } origin-trials-ffi = { path = "../../../../dom/origin-trials/ffi" } jog = { path = "../../../components/glean/bindings/jog" } dap_ffi = { path = "../../../components/telemetry/dap/ffi" } diff --git a/toolkit/library/rust/shared/lib.rs b/toolkit/library/rust/shared/lib.rs index a6dfe60411f9..792edf1239fd 100644 --- a/toolkit/library/rust/shared/lib.rs +++ b/toolkit/library/rust/shared/lib.rs @@ -24,7 +24,6 @@ extern crate cubeb_coreaudio; #[cfg(feature = "cubeb_pulse_rust")] extern crate cubeb_pulse; extern crate data_storage; -extern crate dom_fragmentdirectives; extern crate encoding_glue; extern crate fog_control; extern crate gecko_profiler; diff --git a/xpcom/ds/StaticAtoms.py b/xpcom/ds/StaticAtoms.py index 63ceaf90d2a9..9c653eced322 100644 --- a/xpcom/ds/StaticAtoms.py +++ b/xpcom/ds/StaticAtoms.py @@ -2486,7 +2486,6 @@ STATIC_ATOMS = [ PseudoElementAtom("PseudoElement_firstLine", ":first-line"), PseudoElementAtom("PseudoElement_highlight", ":highlight"), PseudoElementAtom("PseudoElement_selection", ":selection"), - PseudoElementAtom("PseudoElement_targetText", ":target-text"), PseudoElementAtom("PseudoElement_mozFocusInner", ":-moz-focus-inner"), PseudoElementAtom("PseudoElement_mozNumberSpinBox", ":-moz-number-spin-box"), PseudoElementAtom("PseudoElement_mozNumberSpinUp", ":-moz-number-spin-up"),