/* * Copyright (C) 2014-2015 Apple Inc. All rights reserved. * Copyright (C) 2014 Dhi Aurrahman * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef SelectorCheckerTestFunctions_h #define SelectorCheckerTestFunctions_h #include "FocusController.h" #include "HTMLInputElement.h" #include "HTMLOptionElement.h" #include "RenderScrollbar.h" #include "ScrollableArea.h" #include "ScrollbarTheme.h" #include #if ENABLE(VIDEO_TRACK) #include "WebVTTElement.h" #endif namespace WebCore { ALWAYS_INLINE bool isAutofilled(const Element& element) { return is(element) && downcast(element).isAutoFilled(); } ALWAYS_INLINE bool isDefaultButtonForForm(const Element& element) { return element.isDefaultButtonForForm(); } ALWAYS_INLINE bool isDisabled(const Element& element) { return (is(element) || is(element) || is(element)) && element.isDisabledFormControl(); } ALWAYS_INLINE bool isEnabled(const Element& element) { return (is(element) || is(element) || is(element)) && !element.isDisabledFormControl(); } ALWAYS_INLINE bool isMediaDocument(const Element& element) { return element.document().isMediaDocument(); } ALWAYS_INLINE bool isChecked(const Element& element) { // Even though WinIE allows checked and indeterminate to co-exist, the CSS selector spec says that // you can't be both checked and indeterminate. We will behave like WinIE behind the scenes and just // obey the CSS spec here in the test for matching the pseudo. if (is(element)) { auto& inputElement = downcast(element); return inputElement.shouldAppearChecked() && !inputElement.shouldAppearIndeterminate(); } if (is(element)) return const_cast(downcast(element)).selected(); return false; } ALWAYS_INLINE bool isInRange(const Element& element) { return element.isInRange(); } ALWAYS_INLINE bool isOutOfRange(const Element& element) { return element.isOutOfRange(); } ALWAYS_INLINE bool isInvalid(const Element& element) { return element.matchesInvalidPseudoClass(); } ALWAYS_INLINE bool isOptionalFormControl(const Element& element) { return element.isOptionalFormControl(); } ALWAYS_INLINE bool isRequiredFormControl(const Element& element) { return element.isRequiredFormControl(); } ALWAYS_INLINE bool isValid(const Element& element) { return element.matchesValidPseudoClass(); } ALWAYS_INLINE bool isWindowInactive(const Element& element) { auto* page = element.document().page(); if (!page) return false; return !page->focusController().isActive(); } ALWAYS_INLINE bool containslanguageSubtagMatchingRange(StringView language, StringView range, unsigned languageLength, unsigned& position) { unsigned languageSubtagsStartIndex = position; unsigned languageSubtagsEndIndex = languageLength; bool isAsteriskRange = range == "*"; do { if (languageSubtagsStartIndex > 0) languageSubtagsStartIndex += 1; languageSubtagsEndIndex = std::min(language.find('-', languageSubtagsStartIndex), languageLength); if (languageSubtagsStartIndex > languageSubtagsEndIndex) return false; StringView languageSubtag = language.substring(languageSubtagsStartIndex, languageSubtagsEndIndex - languageSubtagsStartIndex); bool isEqual = equalIgnoringASCIICase(range, languageSubtag); if (!isAsteriskRange) { if ((!isEqual && !languageSubtagsStartIndex) || (languageSubtag.length() == 1 && languageSubtagsStartIndex > 0)) return false; } languageSubtagsStartIndex = languageSubtagsEndIndex; if (isEqual || isAsteriskRange) { position = languageSubtagsStartIndex; return true; } } while (languageSubtagsStartIndex < languageLength); return false; } ALWAYS_INLINE bool matchesLangPseudoClass(const Element& element, const Vector& argumentList) { AtomicString language; #if ENABLE(VIDEO_TRACK) if (is(element)) language = downcast(element).language(); else #endif language = element.computeInheritedLanguage(); if (language.isEmpty()) return false; // Implement basic and extended filterings of given language tags // as specified in www.ietf.org/rfc/rfc4647.txt. StringView languageStringView = language.string(); unsigned languageLength = language.length(); for (const AtomicString& range : argumentList) { if (range.isEmpty()) continue; if (range == "*") return true; StringView rangeStringView = range.string(); if (equalIgnoringASCIICase(languageStringView, rangeStringView) && !languageStringView.contains('-')) return true; unsigned rangeLength = rangeStringView.length(); unsigned rangeSubtagsStartIndex = 0; unsigned rangeSubtagsEndIndex = rangeLength; unsigned lastMatchedLanguageSubtagIndex = 0; bool matchedRange = true; do { if (rangeSubtagsStartIndex > 0) rangeSubtagsStartIndex += 1; if (rangeSubtagsStartIndex > languageLength) return false; rangeSubtagsEndIndex = std::min(rangeStringView.find('-', rangeSubtagsStartIndex), rangeLength); StringView rangeSubtag = rangeStringView.substring(rangeSubtagsStartIndex, rangeSubtagsEndIndex - rangeSubtagsStartIndex); if (!containslanguageSubtagMatchingRange(languageStringView, rangeSubtag, languageLength, lastMatchedLanguageSubtagIndex)) { matchedRange = false; break; } rangeSubtagsStartIndex = rangeSubtagsEndIndex; } while (rangeSubtagsStartIndex < rangeLength); if (matchedRange) return true; } return false; } ALWAYS_INLINE bool matchesReadOnlyPseudoClass(const Element& element) { return !element.matchesReadWritePseudoClass(); } ALWAYS_INLINE bool matchesReadWritePseudoClass(const Element& element) { return element.matchesReadWritePseudoClass(); } ALWAYS_INLINE bool shouldAppearIndeterminate(const Element& element) { return element.shouldAppearIndeterminate(); } ALWAYS_INLINE bool scrollbarMatchesEnabledPseudoClass(const SelectorChecker::CheckingContext& context) { return context.scrollbar && context.scrollbar->enabled(); } ALWAYS_INLINE bool scrollbarMatchesDisabledPseudoClass(const SelectorChecker::CheckingContext& context) { return context.scrollbar && !context.scrollbar->enabled(); } ALWAYS_INLINE bool scrollbarMatchesHoverPseudoClass(const SelectorChecker::CheckingContext& context) { if (!context.scrollbar) return false; ScrollbarPart hoveredPart = context.scrollbar->hoveredPart(); if (context.scrollbarPart == ScrollbarBGPart) return hoveredPart != NoPart; if (context.scrollbarPart == TrackBGPart) return hoveredPart == BackTrackPart || hoveredPart == ForwardTrackPart || hoveredPart == ThumbPart; return context.scrollbarPart == hoveredPart; } ALWAYS_INLINE bool scrollbarMatchesActivePseudoClass(const SelectorChecker::CheckingContext& context) { if (!context.scrollbar) return false; ScrollbarPart pressedPart = context.scrollbar->pressedPart(); if (context.scrollbarPart == ScrollbarBGPart) return pressedPart != NoPart; if (context.scrollbarPart == TrackBGPart) return pressedPart == BackTrackPart || pressedPart == ForwardTrackPart || pressedPart == ThumbPart; return context.scrollbarPart == pressedPart; } ALWAYS_INLINE bool scrollbarMatchesHorizontalPseudoClass(const SelectorChecker::CheckingContext& context) { return context.scrollbar && context.scrollbar->orientation() == HorizontalScrollbar; } ALWAYS_INLINE bool scrollbarMatchesVerticalPseudoClass(const SelectorChecker::CheckingContext& context) { return context.scrollbar && context.scrollbar->orientation() == VerticalScrollbar; } ALWAYS_INLINE bool scrollbarMatchesDecrementPseudoClass(const SelectorChecker::CheckingContext& context) { return context.scrollbarPart == BackButtonStartPart || context.scrollbarPart == BackButtonEndPart || context.scrollbarPart == BackTrackPart; } ALWAYS_INLINE bool scrollbarMatchesIncrementPseudoClass(const SelectorChecker::CheckingContext& context) { return context.scrollbarPart == ForwardButtonStartPart || context.scrollbarPart == ForwardButtonEndPart || context.scrollbarPart == ForwardTrackPart; } ALWAYS_INLINE bool scrollbarMatchesStartPseudoClass(const SelectorChecker::CheckingContext& context) { return context.scrollbarPart == BackButtonStartPart || context.scrollbarPart == ForwardButtonStartPart || context.scrollbarPart == BackTrackPart; } ALWAYS_INLINE bool scrollbarMatchesEndPseudoClass(const SelectorChecker::CheckingContext& context) { return context.scrollbarPart == BackButtonEndPart || context.scrollbarPart == ForwardButtonEndPart || context.scrollbarPart == ForwardTrackPart; } ALWAYS_INLINE bool scrollbarMatchesDoubleButtonPseudoClass(const SelectorChecker::CheckingContext& context) { if (!context.scrollbar) return false; ScrollbarButtonsPlacement buttonsPlacement = context.scrollbar->theme().buttonsPlacement(); if (context.scrollbarPart == BackButtonStartPart || context.scrollbarPart == ForwardButtonStartPart || context.scrollbarPart == BackTrackPart) return buttonsPlacement == ScrollbarButtonsDoubleStart || buttonsPlacement == ScrollbarButtonsDoubleBoth; if (context.scrollbarPart == BackButtonEndPart || context.scrollbarPart == ForwardButtonEndPart || context.scrollbarPart == ForwardTrackPart) return buttonsPlacement == ScrollbarButtonsDoubleEnd || buttonsPlacement == ScrollbarButtonsDoubleBoth; return false; } ALWAYS_INLINE bool scrollbarMatchesSingleButtonPseudoClass(const SelectorChecker::CheckingContext& context) { if (!context.scrollbar) return false; ScrollbarButtonsPlacement buttonsPlacement = context.scrollbar->theme().buttonsPlacement(); if (context.scrollbarPart == BackButtonStartPart || context.scrollbarPart == ForwardButtonEndPart || context.scrollbarPart == BackTrackPart || context.scrollbarPart == ForwardTrackPart) return buttonsPlacement == ScrollbarButtonsSingle; return false; } ALWAYS_INLINE bool scrollbarMatchesNoButtonPseudoClass(const SelectorChecker::CheckingContext& context) { if (!context.scrollbar) return false; ScrollbarButtonsPlacement buttonsPlacement = context.scrollbar->theme().buttonsPlacement(); if (context.scrollbarPart == BackTrackPart) return buttonsPlacement == ScrollbarButtonsNone || buttonsPlacement == ScrollbarButtonsDoubleEnd; if (context.scrollbarPart == ForwardTrackPart) return buttonsPlacement == ScrollbarButtonsNone || buttonsPlacement == ScrollbarButtonsDoubleStart; return false; } ALWAYS_INLINE bool scrollbarMatchesCornerPresentPseudoClass(const SelectorChecker::CheckingContext& context) { return context.scrollbar && context.scrollbar->scrollableArea().isScrollCornerVisible(); } #if ENABLE(FULLSCREEN_API) ALWAYS_INLINE bool matchesFullScreenPseudoClass(const Element& element) { // While a Document is in the fullscreen state, and the document's current fullscreen // element is an element in the document, the 'full-screen' pseudoclass applies to // that element. Also, an