⚠ Archived content — this site is no longer maintained.   Current WebKit documentation is at docs.webkit.org.

Changeset 181525 in webkit


Ignore:
Timestamp:
Mar 15, 2015, 11:51:44 PM (11 years ago)
Author:
benjamin@webkit.org
Message:

CSS: fix the case-insensitive matching of the attribute selectors Begin, End and Hyphen
https://bugs.webkit.org/show_bug.cgi?id=142715

Reviewed by Brent Fulgham.

Source/WebCore:

Fix attribute matching with:
-Begin: [a=b].
-End: [a$=b].
-Hyphen: [a|=b].

Tests: fast/selectors/attribute-endswith-value-matching-is-ascii-case-insensitive.html

fast/selectors/attribute-hyphen-value-matching-is-ascii-case-insensitive.html
fast/selectors/attribute-startswith-value-matching-is-ascii-case-insensitive.html

  • css/SelectorChecker.cpp:

(WebCore::attributeValueMatches):
I forgot to change CSSSelector::Exact in my last patch.
The tests could not catch that since we use the CSS JIT almost everywhere.

  • cssjit/SelectorCompiler.cpp:

(WebCore::SelectorCompiler::attributeValueBeginsWith):
(WebCore::SelectorCompiler::attributeValueEndsWith):
(WebCore::SelectorCompiler::attributeValueMatchHyphenRule):

Source/WTF:

Add the necessary infrastructure to test startsWith() and endsWith() with
ASCII case-insentive comparisons.

  • wtf/text/AtomicString.h:

(WTF::AtomicString::startsWith):
(WTF::AtomicString::startsWithIgnoringASCIICase):
(WTF::AtomicString::endsWith):
(WTF::AtomicString::endsWithIgnoringASCIICase):

  • wtf/text/StringCommon.h:

(WTF::loadUnaligned):
(WTF::equal):
I moved the low level equal() code from StringImpl to StringCommon
since it is used by both StringImpl and StringView.

(WTF::equalCommon):
(WTF::equalIgnoringASCIICaseCommon):
Ideally we should drop the "Common" part of the name but StringView
wants this inline for some reason. I prefered keeping the current behavior
since I don't know how StringView's matching performance was evaluated.

(WTF::startsWith):
(WTF::startsWithIgnoringASCIICase):
(WTF::endsWith):
(WTF::endsWithIgnoringASCIICase):
Make all that code shared between StringView and Stringimpl.

  • wtf/text/StringImpl.cpp:

(WTF::StringImpl::startsWith):
(WTF::StringImpl::startsWithIgnoringASCIICase):
(WTF::StringImpl::endsWith):
(WTF::StringImpl::endsWithIgnoringASCIICase):
(WTF::equal):
(WTF::stringImplContentEqual): Deleted.

  • wtf/text/StringImpl.h:

(WTF::loadUnaligned): Deleted.
(WTF::equal): Deleted.

  • wtf/text/StringView.cpp:

(WTF::StringView::startsWith):
(WTF::StringView::startsWithIgnoringASCIICase):
(WTF::StringView::endsWith):
(WTF::StringView::endsWithIgnoringASCIICase):

  • wtf/text/StringView.h:

Since those are new, we can safely make them out-of-line and
evaluate the inlining impact as needed.

  • wtf/text/WTFString.h:

(WTF::String::startsWithIgnoringASCIICase):
(WTF::String::endsWith):
(WTF::String::endsWithIgnoringASCIICase):

Tools:

  • TestWebKitAPI/Tests/WTF/StringImpl.cpp:

(TestWebKitAPI::TEST):

LayoutTests:

  • fast/selectors/attribute-endswith-value-matching-is-ascii-case-insensitive-expected.txt: Added.
  • fast/selectors/attribute-endswith-value-matching-is-ascii-case-insensitive.html: Added.
  • fast/selectors/attribute-hyphen-value-matching-is-ascii-case-insensitive-expected.txt: Added.
  • fast/selectors/attribute-hyphen-value-matching-is-ascii-case-insensitive.html: Added.
  • fast/selectors/attribute-startswith-value-matching-is-ascii-case-insensitive-expected.txt: Added.
  • fast/selectors/attribute-startswith-value-matching-is-ascii-case-insensitive.html: Added.
Location:
trunk
Files:
6 added
14 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r181522 r181525  
     12015-03-15  Benjamin Poulain  <benjamin@webkit.org>
     2
     3        CSS: fix the case-insensitive matching of the attribute selectors Begin, End and Hyphen
     4        https://bugs.webkit.org/show_bug.cgi?id=142715
     5
     6        Reviewed by Brent Fulgham.
     7
     8        * fast/selectors/attribute-endswith-value-matching-is-ascii-case-insensitive-expected.txt: Added.
     9        * fast/selectors/attribute-endswith-value-matching-is-ascii-case-insensitive.html: Added.
     10        * fast/selectors/attribute-hyphen-value-matching-is-ascii-case-insensitive-expected.txt: Added.
     11        * fast/selectors/attribute-hyphen-value-matching-is-ascii-case-insensitive.html: Added.
     12        * fast/selectors/attribute-startswith-value-matching-is-ascii-case-insensitive-expected.txt: Added.
     13        * fast/selectors/attribute-startswith-value-matching-is-ascii-case-insensitive.html: Added.
     14
    1152015-03-15  Brent Fulgham  <bfulgham@apple.com>
    216
  • trunk/Source/WTF/ChangeLog

    r181512 r181525  
     12015-03-15  Benjamin Poulain  <benjamin@webkit.org>
     2
     3        CSS: fix the case-insensitive matching of the attribute selectors Begin, End and Hyphen
     4        https://bugs.webkit.org/show_bug.cgi?id=142715
     5
     6        Reviewed by Brent Fulgham.
     7
     8        Add the necessary infrastructure to test startsWith() and endsWith() with
     9        ASCII case-insentive comparisons.
     10
     11        * wtf/text/AtomicString.h:
     12        (WTF::AtomicString::startsWith):
     13        (WTF::AtomicString::startsWithIgnoringASCIICase):
     14        (WTF::AtomicString::endsWith):
     15        (WTF::AtomicString::endsWithIgnoringASCIICase):
     16
     17        * wtf/text/StringCommon.h:
     18        (WTF::loadUnaligned):
     19        (WTF::equal):
     20        I moved the low level equal() code from StringImpl to StringCommon
     21        since it is used by both StringImpl and StringView.
     22
     23        (WTF::equalCommon):
     24        (WTF::equalIgnoringASCIICaseCommon):
     25        Ideally we should drop the "Common" part of the name but StringView
     26        wants this inline for some reason. I prefered keeping the current behavior
     27        since I don't know how StringView's matching performance was evaluated.
     28
     29        (WTF::startsWith):
     30        (WTF::startsWithIgnoringASCIICase):
     31        (WTF::endsWith):
     32        (WTF::endsWithIgnoringASCIICase):
     33        Make all that code shared between StringView and Stringimpl.
     34
     35        * wtf/text/StringImpl.cpp:
     36        (WTF::StringImpl::startsWith):
     37        (WTF::StringImpl::startsWithIgnoringASCIICase):
     38        (WTF::StringImpl::endsWith):
     39        (WTF::StringImpl::endsWithIgnoringASCIICase):
     40        (WTF::equal):
     41        (WTF::stringImplContentEqual): Deleted.
     42        * wtf/text/StringImpl.h:
     43        (WTF::loadUnaligned): Deleted.
     44        (WTF::equal): Deleted.
     45
     46        * wtf/text/StringView.cpp:
     47        (WTF::StringView::startsWith):
     48        (WTF::StringView::startsWithIgnoringASCIICase):
     49        (WTF::StringView::endsWith):
     50        (WTF::StringView::endsWithIgnoringASCIICase):
     51        * wtf/text/StringView.h:
     52        Since those are new, we can safely make them out-of-line and
     53        evaluate the inlining impact as needed.
     54
     55        * wtf/text/WTFString.h:
     56        (WTF::String::startsWithIgnoringASCIICase):
     57        (WTF::String::endsWith):
     58        (WTF::String::endsWithIgnoringASCIICase):
     59
    1602015-03-15  Benjamin Poulain  <bpoulain@apple.com>
    261
  • trunk/Source/WTF/wtf/text/AtomicString.h

    r181512 r181525  
    127127        { return m_string.find(matchFunction, start); }
    128128
    129     bool startsWith(const String& s, bool caseSensitive = true) const
     129    bool startsWith(const String& s) const
     130        { return m_string.startsWith(s); }
     131    bool startsWithIgnoringASCIICase(const String& s) const
     132        { return m_string.startsWithIgnoringASCIICase(s); }
     133    bool startsWith(const String& s, bool caseSensitive) const
    130134        { return m_string.startsWith(s, caseSensitive); }
    131135    bool startsWith(UChar character) const
     
    135139        { return m_string.startsWith<matchLength>(prefix, caseSensitive); }
    136140
    137     bool endsWith(const String& s, bool caseSensitive = true) const
     141    bool endsWith(const String& s) const
     142        { return m_string.endsWith(s); }
     143    bool endsWithIgnoringASCIICase(const String& s) const
     144        { return m_string.endsWithIgnoringASCIICase(s); }
     145    bool endsWith(const String& s, bool caseSensitive) const
    138146        { return m_string.endsWith(s, caseSensitive); }
    139147    bool endsWith(UChar character) const
  • trunk/Source/WTF/wtf/text/StringCommon.h

    r181512 r181525  
    3232namespace WTF {
    3333
     34template<typename T>
     35inline T loadUnaligned(const char* s)
     36{
     37#if COMPILER(CLANG)
     38    T tmp;
     39    memcpy(&tmp, s, sizeof(T));
     40    return tmp;
     41#else
     42    // This may result in undefined behavior due to unaligned access.
     43    return *reinterpret_cast<const T*>(s);
     44#endif
     45}
     46
     47// Do comparisons 8 or 4 bytes-at-a-time on architectures where it's safe.
     48#if (CPU(X86_64) || CPU(ARM64)) && !ASAN_ENABLED
     49ALWAYS_INLINE bool equal(const LChar* aLChar, const LChar* bLChar, unsigned length)
     50{
     51    unsigned dwordLength = length >> 3;
     52
     53    const char* a = reinterpret_cast<const char*>(aLChar);
     54    const char* b = reinterpret_cast<const char*>(bLChar);
     55
     56    if (dwordLength) {
     57        for (unsigned i = 0; i != dwordLength; ++i) {
     58            if (loadUnaligned<uint64_t>(a) != loadUnaligned<uint64_t>(b))
     59                return false;
     60
     61            a += sizeof(uint64_t);
     62            b += sizeof(uint64_t);
     63        }
     64    }
     65
     66    if (length & 4) {
     67        if (loadUnaligned<uint32_t>(a) != loadUnaligned<uint32_t>(b))
     68            return false;
     69
     70        a += sizeof(uint32_t);
     71        b += sizeof(uint32_t);
     72    }
     73
     74    if (length & 2) {
     75        if (loadUnaligned<uint16_t>(a) != loadUnaligned<uint16_t>(b))
     76            return false;
     77
     78        a += sizeof(uint16_t);
     79        b += sizeof(uint16_t);
     80    }
     81
     82    if (length & 1 && (*reinterpret_cast<const LChar*>(a) != *reinterpret_cast<const LChar*>(b)))
     83        return false;
     84
     85    return true;
     86}
     87
     88ALWAYS_INLINE bool equal(const UChar* aUChar, const UChar* bUChar, unsigned length)
     89{
     90    unsigned dwordLength = length >> 2;
     91
     92    const char* a = reinterpret_cast<const char*>(aUChar);
     93    const char* b = reinterpret_cast<const char*>(bUChar);
     94
     95    if (dwordLength) {
     96        for (unsigned i = 0; i != dwordLength; ++i) {
     97            if (loadUnaligned<uint64_t>(a) != loadUnaligned<uint64_t>(b))
     98                return false;
     99
     100            a += sizeof(uint64_t);
     101            b += sizeof(uint64_t);
     102        }
     103    }
     104
     105    if (length & 2) {
     106        if (loadUnaligned<uint32_t>(a) != loadUnaligned<uint32_t>(b))
     107            return false;
     108
     109        a += sizeof(uint32_t);
     110        b += sizeof(uint32_t);
     111    }
     112
     113    if (length & 1 && (*reinterpret_cast<const UChar*>(a) != *reinterpret_cast<const UChar*>(b)))
     114        return false;
     115
     116    return true;
     117}
     118#elif CPU(X86) && !ASAN_ENABLED
     119ALWAYS_INLINE bool equal(const LChar* aLChar, const LChar* bLChar, unsigned length)
     120{
     121    const char* a = reinterpret_cast<const char*>(aLChar);
     122    const char* b = reinterpret_cast<const char*>(bLChar);
     123
     124    unsigned wordLength = length >> 2;
     125    for (unsigned i = 0; i != wordLength; ++i) {
     126        if (loadUnaligned<uint32_t>(a) != loadUnaligned<uint32_t>(b))
     127            return false;
     128        a += sizeof(uint32_t);
     129        b += sizeof(uint32_t);
     130    }
     131
     132    length &= 3;
     133
     134    if (length) {
     135        const LChar* aRemainder = reinterpret_cast<const LChar*>(a);
     136        const LChar* bRemainder = reinterpret_cast<const LChar*>(b);
     137
     138        for (unsigned i = 0; i <  length; ++i) {
     139            if (aRemainder[i] != bRemainder[i])
     140                return false;
     141        }
     142    }
     143
     144    return true;
     145}
     146
     147ALWAYS_INLINE bool equal(const UChar* aUChar, const UChar* bUChar, unsigned length)
     148{
     149    const char* a = reinterpret_cast<const char*>(aUChar);
     150    const char* b = reinterpret_cast<const char*>(bUChar);
     151
     152    unsigned wordLength = length >> 1;
     153    for (unsigned i = 0; i != wordLength; ++i) {
     154        if (loadUnaligned<uint32_t>(a) != loadUnaligned<uint32_t>(b))
     155            return false;
     156        a += sizeof(uint32_t);
     157        b += sizeof(uint32_t);
     158    }
     159
     160    if (length & 1 && *reinterpret_cast<const UChar*>(a) != *reinterpret_cast<const UChar*>(b))
     161        return false;
     162
     163    return true;
     164}
     165#elif PLATFORM(IOS) && WTF_ARM_ARCH_AT_LEAST(7) && !ASAN_ENABLED
     166ALWAYS_INLINE bool equal(const LChar* a, const LChar* b, unsigned length)
     167{
     168    bool isEqual = false;
     169    uint32_t aValue;
     170    uint32_t bValue;
     171    asm("subs   %[length], #4\n"
     172        "blo    2f\n"
     173
     174        "0:\n" // Label 0 = Start of loop over 32 bits.
     175        "ldr    %[aValue], [%[a]], #4\n"
     176        "ldr    %[bValue], [%[b]], #4\n"
     177        "cmp    %[aValue], %[bValue]\n"
     178        "bne    66f\n"
     179        "subs   %[length], #4\n"
     180        "bhs    0b\n"
     181
     182        // At this point, length can be:
     183        // -0: 00000000000000000000000000000000 (0 bytes left)
     184        // -1: 11111111111111111111111111111111 (3 bytes left)
     185        // -2: 11111111111111111111111111111110 (2 bytes left)
     186        // -3: 11111111111111111111111111111101 (1 byte left)
     187        // -4: 11111111111111111111111111111100 (length was 0)
     188        // The pointers are at the correct position.
     189        "2:\n" // Label 2 = End of loop over 32 bits, check for pair of characters.
     190        "tst    %[length], #2\n"
     191        "beq    1f\n"
     192        "ldrh   %[aValue], [%[a]], #2\n"
     193        "ldrh   %[bValue], [%[b]], #2\n"
     194        "cmp    %[aValue], %[bValue]\n"
     195        "bne    66f\n"
     196
     197        "1:\n" // Label 1 = Check for a single character left.
     198        "tst    %[length], #1\n"
     199        "beq    42f\n"
     200        "ldrb   %[aValue], [%[a]]\n"
     201        "ldrb   %[bValue], [%[b]]\n"
     202        "cmp    %[aValue], %[bValue]\n"
     203        "bne    66f\n"
     204
     205        "42:\n" // Label 42 = Success.
     206        "mov    %[isEqual], #1\n"
     207        "66:\n" // Label 66 = End without changing isEqual to 1.
     208        : [length]"+r"(length), [isEqual]"+r"(isEqual), [a]"+r"(a), [b]"+r"(b), [aValue]"+r"(aValue), [bValue]"+r"(bValue)
     209        :
     210        :
     211        );
     212    return isEqual;
     213}
     214
     215ALWAYS_INLINE bool equal(const UChar* a, const UChar* b, unsigned length)
     216{
     217    bool isEqual = false;
     218    uint32_t aValue;
     219    uint32_t bValue;
     220    asm("subs   %[length], #2\n"
     221        "blo    1f\n"
     222
     223        "0:\n" // Label 0 = Start of loop over 32 bits.
     224        "ldr    %[aValue], [%[a]], #4\n"
     225        "ldr    %[bValue], [%[b]], #4\n"
     226        "cmp    %[aValue], %[bValue]\n"
     227        "bne    66f\n"
     228        "subs   %[length], #2\n"
     229        "bhs    0b\n"
     230
     231        // At this point, length can be:
     232        // -0: 00000000000000000000000000000000 (0 bytes left)
     233        // -1: 11111111111111111111111111111111 (1 character left, 2 bytes)
     234        // -2: 11111111111111111111111111111110 (length was zero)
     235        // The pointers are at the correct position.
     236        "1:\n" // Label 1 = Check for a single character left.
     237        "tst    %[length], #1\n"
     238        "beq    42f\n"
     239        "ldrh   %[aValue], [%[a]]\n"
     240        "ldrh   %[bValue], [%[b]]\n"
     241        "cmp    %[aValue], %[bValue]\n"
     242        "bne    66f\n"
     243
     244        "42:\n" // Label 42 = Success.
     245        "mov    %[isEqual], #1\n"
     246        "66:\n" // Label 66 = End without changing isEqual to 1.
     247        : [length]"+r"(length), [isEqual]"+r"(isEqual), [a]"+r"(a), [b]"+r"(b), [aValue]"+r"(aValue), [bValue]"+r"(bValue)
     248        :
     249        :
     250        );
     251    return isEqual;
     252}
     253#elif !ASAN_ENABLED
     254ALWAYS_INLINE bool equal(const LChar* a, const LChar* b, unsigned length) { return !memcmp(a, b, length); }
     255ALWAYS_INLINE bool equal(const UChar* a, const UChar* b, unsigned length) { return !memcmp(a, b, length * sizeof(UChar)); }
     256#else
     257ALWAYS_INLINE bool equal(const LChar* a, const LChar* b, unsigned length)
     258{
     259    for (unsigned i = 0; i < length; ++i) {
     260        if (a[i] != b[i])
     261            return false;
     262    }
     263    return true;
     264}
     265ALWAYS_INLINE bool equal(const UChar* a, const UChar* b, unsigned length)
     266{
     267    for (unsigned i = 0; i < length; ++i) {
     268        if (a[i] != b[i])
     269            return false;
     270    }
     271    return true;
     272}
     273#endif
     274
     275ALWAYS_INLINE bool equal(const LChar* a, const UChar* b, unsigned length)
     276{
     277    for (unsigned i = 0; i < length; ++i) {
     278        if (a[i] != b[i])
     279            return false;
     280    }
     281    return true;
     282}
     283
     284ALWAYS_INLINE bool equal(const UChar* a, const LChar* b, unsigned length) { return equal(b, a, length); }
     285
     286template<typename StringClassA, typename StringClassB>
     287ALWAYS_INLINE bool equalCommon(const StringClassA& a, const StringClassB& b)
     288{
     289    unsigned length = a.length();
     290    if (length != b.length())
     291        return false;
     292
     293    if (a.is8Bit()) {
     294        if (b.is8Bit())
     295            return equal(a.characters8(), b.characters8(), length);
     296
     297        return equal(a.characters8(), b.characters16(), length);
     298    }
     299
     300    if (b.is8Bit())
     301        return equal(a.characters16(), b.characters8(), length);
     302
     303    return equal(a.characters16(), b.characters16(), length);
     304}
     305
     306template<typename StringClassA, typename StringClassB>
     307ALWAYS_INLINE bool equalCommon(const StringClassA* a, const StringClassB* b)
     308{
     309    if (a == b)
     310        return true;
     311    if (!a || !b)
     312        return false;
     313    return equal(*a, *b);
     314}
     315
    34316template<typename CharacterTypeA, typename CharacterTypeB>
    35317inline bool equalIgnoringASCIICase(const CharacterTypeA* a, const CharacterTypeB* b, unsigned length)
     
    42324}
    43325
    44 template<typename StringClass>
    45 bool equalIgnoringASCIICaseCommon(const StringClass& a, const StringClass& b)
     326template<typename StringClassA, typename StringClassB>
     327bool equalIgnoringASCIICaseCommon(const StringClassA& a, const StringClassB& b)
    46328{
    47329    unsigned length = a.length();
     
    62344}
    63345
     346template<typename StringClassA, typename StringClassB>
     347bool startsWith(const StringClassA& reference, const StringClassB& prefix)
     348{
     349    unsigned prefixLength = prefix.length();
     350    if (prefixLength > reference.length())
     351        return false;
     352
     353    if (reference.is8Bit()) {
     354        if (prefix.is8Bit())
     355            return equal(reference.characters8(), prefix.characters8(), prefixLength);
     356        return equal(reference.characters8(), prefix.characters16(), prefixLength);
     357    }
     358    if (prefix.is8Bit())
     359        return equal(reference.characters16(), prefix.characters8(), prefixLength);
     360    return equal(reference.characters16(), prefix.characters16(), prefixLength);
     361}
     362
     363template<typename StringClassA, typename StringClassB>
     364bool startsWithIgnoringASCIICase(const StringClassA& reference, const StringClassB& prefix)
     365{
     366    unsigned prefixLength = prefix.length();
     367    if (prefixLength > reference.length())
     368        return false;
     369
     370    if (reference.is8Bit()) {
     371        if (prefix.is8Bit())
     372            return equalIgnoringASCIICase(reference.characters8(), prefix.characters8(), prefixLength);
     373        return equalIgnoringASCIICase(reference.characters8(), prefix.characters16(), prefixLength);
     374    }
     375    if (prefix.is8Bit())
     376        return equalIgnoringASCIICase(reference.characters16(), prefix.characters8(), prefixLength);
     377    return equalIgnoringASCIICase(reference.characters16(), prefix.characters16(), prefixLength);
     378}
     379
     380template<typename StringClassA, typename StringClassB>
     381bool endsWith(const StringClassA& reference, const StringClassB& suffix)
     382{
     383    unsigned suffixLength = suffix.length();
     384    unsigned referenceLength = reference.length();
     385    if (suffixLength > referenceLength)
     386        return false;
     387
     388    unsigned startOffset = referenceLength - suffixLength;
     389
     390    if (reference.is8Bit()) {
     391        if (suffix.is8Bit())
     392            return equal(reference.characters8() + startOffset, suffix.characters8(), suffixLength);
     393        return equal(reference.characters8() + startOffset, suffix.characters16(), suffixLength);
     394    }
     395    if (suffix.is8Bit())
     396        return equal(reference.characters16() + startOffset, suffix.characters8(), suffixLength);
     397    return equal(reference.characters16() + startOffset, suffix.characters16(), suffixLength);
     398}
     399
     400template<typename StringClassA, typename StringClassB>
     401bool endsWithIgnoringASCIICase(const StringClassA& reference, const StringClassB& suffix)
     402{
     403    unsigned suffixLength = suffix.length();
     404    unsigned referenceLength = reference.length();
     405    if (suffixLength > referenceLength)
     406        return false;
     407
     408    unsigned startOffset = referenceLength - suffixLength;
     409
     410    if (reference.is8Bit()) {
     411        if (suffix.is8Bit())
     412            return equalIgnoringASCIICase(reference.characters8() + startOffset, suffix.characters8(), suffixLength);
     413        return equalIgnoringASCIICase(reference.characters8() + startOffset, suffix.characters16(), suffixLength);
     414    }
     415    if (suffix.is8Bit())
     416        return equalIgnoringASCIICase(reference.characters16() + startOffset, suffix.characters8(), suffixLength);
     417    return equalIgnoringASCIICase(reference.characters16() + startOffset, suffix.characters16(), suffixLength);
     418}
     419
    64420}
    65421
  • trunk/Source/WTF/wtf/text/StringImpl.cpp

    r181512 r181525  
    13811381    if (!str)
    13821382        return false;
    1383 
    1384     if (str->length() > length())
     1383    return ::WTF::startsWith(*this, *str);
     1384}
     1385
     1386bool StringImpl::startsWith(const StringImpl& str) const
     1387{
     1388    return ::WTF::startsWith(*this, str);
     1389}
     1390
     1391bool StringImpl::startsWithIgnoringASCIICase(const StringImpl* prefix) const
     1392{
     1393    if (!prefix)
    13851394        return false;
    13861395
    1387     if (is8Bit()) {
    1388         if (str->is8Bit())
    1389             return equal(characters8(), str->characters8(), str->length());
    1390         return equal(characters8(), str->characters16(), str->length());
    1391     }
    1392     if (str->is8Bit())
    1393         return equal(characters16(), str->characters8(), str->length());
    1394     return equal(characters16(), str->characters16(), str->length());
     1396    return ::WTF::startsWithIgnoringASCIICase(*this, *prefix);
     1397}
     1398
     1399bool StringImpl::startsWithIgnoringASCIICase(const StringImpl& prefix) const
     1400{
     1401    return ::WTF::startsWithIgnoringASCIICase(*this, prefix);
    13951402}
    13961403
     
    14131420}
    14141421
     1422bool StringImpl::endsWith(StringImpl* suffix)
     1423{
     1424    if (!suffix)
     1425        return false;
     1426
     1427    return ::WTF::endsWith(*this, *suffix);
     1428}
     1429
     1430bool StringImpl::endsWith(StringImpl& suffix)
     1431{
     1432    return ::WTF::endsWith(*this, suffix);
     1433}
     1434
    14151435bool StringImpl::endsWith(StringImpl* matchString, bool caseSensitive)
    14161436{
     
    14211441    }
    14221442    return false;
     1443}
     1444
     1445bool StringImpl::endsWithIgnoringASCIICase(const StringImpl* suffix) const
     1446{
     1447    if (!suffix)
     1448        return false;
     1449
     1450    return ::WTF::endsWithIgnoringASCIICase(*this, *suffix);
     1451}
     1452
     1453bool StringImpl::endsWithIgnoringASCIICase(const StringImpl& suffix) const
     1454{
     1455    return ::WTF::endsWithIgnoringASCIICase(*this, suffix);
    14231456}
    14241457
     
    18251858}
    18261859
    1827 static ALWAYS_INLINE bool stringImplContentEqual(const StringImpl& a, const StringImpl& b)
    1828 {
    1829     unsigned aLength = a.length();
    1830     unsigned bLength = b.length();
    1831     if (aLength != bLength)
    1832         return false;
    1833 
    1834     if (a.is8Bit()) {
    1835         if (b.is8Bit())
    1836             return equal(a.characters8(), b.characters8(), aLength);
    1837 
    1838         return equal(a.characters8(), b.characters16(), aLength);
    1839     }
    1840 
    1841     if (b.is8Bit())
    1842         return equal(a.characters16(), b.characters8(), aLength);
    1843 
    1844     return equal(a.characters16(), b.characters16(), aLength);
    1845 }
    1846 
    18471860bool equal(const StringImpl* a, const StringImpl* b)
    18481861{
    1849     if (a == b)
    1850         return true;
    1851     if (!a || !b)
    1852         return false;
    1853 
    1854     return stringImplContentEqual(*a, *b);
     1862    return equalCommon(a, b);
    18551863}
    18561864
     
    19171925bool equal(const StringImpl& a, const StringImpl& b)
    19181926{
    1919     if (&a == &b)
    1920         return true;
    1921 
    1922     return stringImplContentEqual(a, b);
     1927    return equalCommon(a, b);
    19231928}
    19241929
  • trunk/Source/WTF/wtf/text/StringImpl.h

    r181512 r181525  
    670670
    671671    WTF_EXPORT_STRING_API bool startsWith(const StringImpl*) const;
     672    WTF_EXPORT_STRING_API bool startsWith(const StringImpl&) const;
     673    WTF_EXPORT_STRING_API bool startsWithIgnoringASCIICase(const StringImpl*) const;
     674    WTF_EXPORT_STRING_API bool startsWithIgnoringASCIICase(const StringImpl&) const;
    672675    bool startsWith(StringImpl* str, bool caseSensitive) { return caseSensitive ? startsWith(str) : (reverseFindIgnoringCase(str, 0) == 0); }
    673676    WTF_EXPORT_STRING_API bool startsWith(UChar) const;
     
    677680    WTF_EXPORT_STRING_API bool hasInfixStartingAt(const StringImpl&, unsigned startOffset) const;
    678681
    679     WTF_EXPORT_STRING_API bool endsWith(StringImpl*, bool caseSensitive = true);
     682    WTF_EXPORT_STRING_API bool endsWith(StringImpl*);
     683    WTF_EXPORT_STRING_API bool endsWith(StringImpl&);
     684    WTF_EXPORT_STRING_API bool endsWithIgnoringASCIICase(const StringImpl*) const;
     685    WTF_EXPORT_STRING_API bool endsWithIgnoringASCIICase(const StringImpl&) const;
     686    WTF_EXPORT_STRING_API bool endsWith(StringImpl*, bool caseSensitive);
    680687    WTF_EXPORT_STRING_API bool endsWith(UChar) const;
    681688    WTF_EXPORT_STRING_API bool endsWith(const char*, unsigned matchLength, bool caseSensitive) const;
     
    862869WTF_EXPORT_STRING_API bool equal(const StringImpl& a, const StringImpl& b);
    863870
    864 template<typename T>
    865 inline T loadUnaligned(const char* s)
    866 {
    867 #if COMPILER(CLANG)
    868     T tmp;
    869     memcpy(&tmp, s, sizeof(T));
    870     return tmp;
    871 #else
    872     // This may result in undefined behavior due to unaligned access.
    873     return *reinterpret_cast<const T*>(s);
    874 #endif
    875 }
    876 
    877 // Do comparisons 8 or 4 bytes-at-a-time on architectures where it's safe.
    878 #if (CPU(X86_64) || CPU(ARM64)) && !ASAN_ENABLED
    879 ALWAYS_INLINE bool equal(const LChar* aLChar, const LChar* bLChar, unsigned length)
    880 {
    881     unsigned dwordLength = length >> 3;
    882 
    883     const char* a = reinterpret_cast<const char*>(aLChar);
    884     const char* b = reinterpret_cast<const char*>(bLChar);
    885 
    886     if (dwordLength) {
    887         for (unsigned i = 0; i != dwordLength; ++i) {
    888             if (loadUnaligned<uint64_t>(a) != loadUnaligned<uint64_t>(b))
    889                 return false;
    890 
    891             a += sizeof(uint64_t);
    892             b += sizeof(uint64_t);
    893         }
    894     }
    895 
    896     if (length & 4) {
    897         if (loadUnaligned<uint32_t>(a) != loadUnaligned<uint32_t>(b))
    898             return false;
    899 
    900         a += sizeof(uint32_t);
    901         b += sizeof(uint32_t);
    902     }
    903 
    904     if (length & 2) {
    905         if (loadUnaligned<uint16_t>(a) != loadUnaligned<uint16_t>(b))
    906             return false;
    907 
    908         a += sizeof(uint16_t);
    909         b += sizeof(uint16_t);
    910     }
    911 
    912     if (length & 1 && (*reinterpret_cast<const LChar*>(a) != *reinterpret_cast<const LChar*>(b)))
    913         return false;
    914 
    915     return true;
    916 }
    917 
    918 ALWAYS_INLINE bool equal(const UChar* aUChar, const UChar* bUChar, unsigned length)
    919 {
    920     unsigned dwordLength = length >> 2;
    921 
    922     const char* a = reinterpret_cast<const char*>(aUChar);
    923     const char* b = reinterpret_cast<const char*>(bUChar);
    924 
    925     if (dwordLength) {
    926         for (unsigned i = 0; i != dwordLength; ++i) {
    927             if (loadUnaligned<uint64_t>(a) != loadUnaligned<uint64_t>(b))
    928                 return false;
    929 
    930             a += sizeof(uint64_t);
    931             b += sizeof(uint64_t);
    932         }
    933     }
    934 
    935     if (length & 2) {
    936         if (loadUnaligned<uint32_t>(a) != loadUnaligned<uint32_t>(b))
    937             return false;
    938 
    939         a += sizeof(uint32_t);
    940         b += sizeof(uint32_t);
    941     }
    942 
    943     if (length & 1 && (*reinterpret_cast<const UChar*>(a) != *reinterpret_cast<const UChar*>(b)))
    944         return false;
    945 
    946     return true;
    947 }
    948 #elif CPU(X86) && !ASAN_ENABLED
    949 ALWAYS_INLINE bool equal(const LChar* aLChar, const LChar* bLChar, unsigned length)
    950 {
    951     const char* a = reinterpret_cast<const char*>(aLChar);
    952     const char* b = reinterpret_cast<const char*>(bLChar);
    953 
    954     unsigned wordLength = length >> 2;
    955     for (unsigned i = 0; i != wordLength; ++i) {
    956         if (loadUnaligned<uint32_t>(a) != loadUnaligned<uint32_t>(b))
    957             return false;
    958         a += sizeof(uint32_t);
    959         b += sizeof(uint32_t);
    960     }
    961 
    962     length &= 3;
    963 
    964     if (length) {
    965         const LChar* aRemainder = reinterpret_cast<const LChar*>(a);
    966         const LChar* bRemainder = reinterpret_cast<const LChar*>(b);
    967 
    968         for (unsigned i = 0; i <  length; ++i) {
    969             if (aRemainder[i] != bRemainder[i])
    970                 return false;
    971         }
    972     }
    973 
    974     return true;
    975 }
    976 
    977 ALWAYS_INLINE bool equal(const UChar* aUChar, const UChar* bUChar, unsigned length)
    978 {
    979     const char* a = reinterpret_cast<const char*>(aUChar);
    980     const char* b = reinterpret_cast<const char*>(bUChar);
    981 
    982     unsigned wordLength = length >> 1;
    983     for (unsigned i = 0; i != wordLength; ++i) {
    984         if (loadUnaligned<uint32_t>(a) != loadUnaligned<uint32_t>(b))
    985             return false;
    986         a += sizeof(uint32_t);
    987         b += sizeof(uint32_t);
    988     }
    989 
    990     if (length & 1 && *reinterpret_cast<const UChar*>(a) != *reinterpret_cast<const UChar*>(b))
    991         return false;
    992 
    993     return true;
    994 }
    995 #elif PLATFORM(IOS) && WTF_ARM_ARCH_AT_LEAST(7) && !ASAN_ENABLED
    996 ALWAYS_INLINE bool equal(const LChar* a, const LChar* b, unsigned length)
    997 {
    998     bool isEqual = false;
    999     uint32_t aValue;
    1000     uint32_t bValue;
    1001     asm("subs   %[length], #4\n"
    1002         "blo    2f\n"
    1003 
    1004         "0:\n" // Label 0 = Start of loop over 32 bits.
    1005         "ldr    %[aValue], [%[a]], #4\n"
    1006         "ldr    %[bValue], [%[b]], #4\n"
    1007         "cmp    %[aValue], %[bValue]\n"
    1008         "bne    66f\n"
    1009         "subs   %[length], #4\n"
    1010         "bhs    0b\n"
    1011 
    1012         // At this point, length can be:
    1013         // -0: 00000000000000000000000000000000 (0 bytes left)
    1014         // -1: 11111111111111111111111111111111 (3 bytes left)
    1015         // -2: 11111111111111111111111111111110 (2 bytes left)
    1016         // -3: 11111111111111111111111111111101 (1 byte left)
    1017         // -4: 11111111111111111111111111111100 (length was 0)
    1018         // The pointers are at the correct position.
    1019         "2:\n" // Label 2 = End of loop over 32 bits, check for pair of characters.
    1020         "tst    %[length], #2\n"
    1021         "beq    1f\n"
    1022         "ldrh   %[aValue], [%[a]], #2\n"
    1023         "ldrh   %[bValue], [%[b]], #2\n"
    1024         "cmp    %[aValue], %[bValue]\n"
    1025         "bne    66f\n"
    1026 
    1027         "1:\n" // Label 1 = Check for a single character left.
    1028         "tst    %[length], #1\n"
    1029         "beq    42f\n"
    1030         "ldrb   %[aValue], [%[a]]\n"
    1031         "ldrb   %[bValue], [%[b]]\n"
    1032         "cmp    %[aValue], %[bValue]\n"
    1033         "bne    66f\n"
    1034 
    1035         "42:\n" // Label 42 = Success.
    1036         "mov    %[isEqual], #1\n"
    1037         "66:\n" // Label 66 = End without changing isEqual to 1.
    1038         : [length]"+r"(length), [isEqual]"+r"(isEqual), [a]"+r"(a), [b]"+r"(b), [aValue]"+r"(aValue), [bValue]"+r"(bValue)
    1039         :
    1040         :
    1041         );
    1042     return isEqual;
    1043 }
    1044 
    1045 ALWAYS_INLINE bool equal(const UChar* a, const UChar* b, unsigned length)
    1046 {
    1047     bool isEqual = false;
    1048     uint32_t aValue;
    1049     uint32_t bValue;
    1050     asm("subs   %[length], #2\n"
    1051         "blo    1f\n"
    1052 
    1053         "0:\n" // Label 0 = Start of loop over 32 bits.
    1054         "ldr    %[aValue], [%[a]], #4\n"
    1055         "ldr    %[bValue], [%[b]], #4\n"
    1056         "cmp    %[aValue], %[bValue]\n"
    1057         "bne    66f\n"
    1058         "subs   %[length], #2\n"
    1059         "bhs    0b\n"
    1060 
    1061         // At this point, length can be:
    1062         // -0: 00000000000000000000000000000000 (0 bytes left)
    1063         // -1: 11111111111111111111111111111111 (1 character left, 2 bytes)
    1064         // -2: 11111111111111111111111111111110 (length was zero)
    1065         // The pointers are at the correct position.
    1066         "1:\n" // Label 1 = Check for a single character left.
    1067         "tst    %[length], #1\n"
    1068         "beq    42f\n"
    1069         "ldrh   %[aValue], [%[a]]\n"
    1070         "ldrh   %[bValue], [%[b]]\n"
    1071         "cmp    %[aValue], %[bValue]\n"
    1072         "bne    66f\n"
    1073 
    1074         "42:\n" // Label 42 = Success.
    1075         "mov    %[isEqual], #1\n"
    1076         "66:\n" // Label 66 = End without changing isEqual to 1.
    1077         : [length]"+r"(length), [isEqual]"+r"(isEqual), [a]"+r"(a), [b]"+r"(b), [aValue]"+r"(aValue), [bValue]"+r"(bValue)
    1078         :
    1079         :
    1080         );
    1081     return isEqual;
    1082 }
    1083 #elif !ASAN_ENABLED
    1084 ALWAYS_INLINE bool equal(const LChar* a, const LChar* b, unsigned length) { return !memcmp(a, b, length); }
    1085 ALWAYS_INLINE bool equal(const UChar* a, const UChar* b, unsigned length) { return !memcmp(a, b, length * sizeof(UChar)); }
    1086 #else
    1087 ALWAYS_INLINE bool equal(const LChar* a, const LChar* b, unsigned length)
    1088 {
    1089     for (unsigned i = 0; i < length; ++i) {
    1090         if (a[i] != b[i])
    1091             return false;
    1092     }
    1093     return true;
    1094 }
    1095 ALWAYS_INLINE bool equal(const UChar* a, const UChar* b, unsigned length)
    1096 {
    1097     for (unsigned i = 0; i < length; ++i) {
    1098         if (a[i] != b[i])
    1099             return false;
    1100     }
    1101     return true;
    1102 }
    1103 #endif
    1104 
    1105 ALWAYS_INLINE bool equal(const LChar* a, const UChar* b, unsigned length)
    1106 {
    1107     for (unsigned i = 0; i < length; ++i) {
    1108         if (a[i] != b[i])
    1109             return false;
    1110     }
    1111     return true;
    1112 }
    1113 
    1114 ALWAYS_INLINE bool equal(const UChar* a, const LChar* b, unsigned length) { return equal(b, a, length); }
    1115 
    1116871WTF_EXPORT_STRING_API bool equalIgnoringCase(const StringImpl*, const StringImpl*);
    1117872WTF_EXPORT_STRING_API bool equalIgnoringCase(const StringImpl*, const LChar*);
  • trunk/Source/WTF/wtf/text/StringView.cpp

    r174395 r181525  
    119119}
    120120
     121bool StringView::startsWith(const StringView& prefix) const
     122{
     123    return ::WTF::startsWith(*this, prefix);
     124}
     125
     126bool StringView::startsWithIgnoringASCIICase(const StringView& prefix) const
     127{
     128    return ::WTF::endsWithIgnoringASCIICase(*this, prefix);
     129}
     130
     131bool StringView::endsWith(const StringView& prefix) const
     132{
     133    return ::WTF::startsWith(*this, prefix);
     134}
     135
     136bool StringView::endsWithIgnoringASCIICase(const StringView& prefix) const
     137{
     138    return ::WTF::endsWithIgnoringASCIICase(*this, prefix);
     139}
     140
    121141}
    122142
  • trunk/Source/WTF/wtf/text/StringView.h

    r181512 r181525  
    107107    bool contains(UChar) const;
    108108
     109    bool startsWith(const StringView&) const;
     110    bool startsWithIgnoringASCIICase(const StringView&) const;
     111
     112    bool endsWith(const StringView&) const;
     113    bool endsWithIgnoringASCIICase(const StringView&) const;
     114
    109115    int toInt(bool& isValid) const;
    110116    float toFloat(bool& isValid) const;
     
    470476inline bool equal(StringView a, StringView b)
    471477{
    472     unsigned aLength = a.length();
    473     unsigned bLength = b.length();
    474     if (aLength != bLength)
    475         return false;
    476 
    477     if (a.is8Bit()) {
    478         if (b.is8Bit())
    479             return equal(a.characters8(), b.characters8(), aLength);
    480 
    481         return equal(a.characters8(), b.characters16(), aLength);
    482     }
    483 
    484     if (b.is8Bit())
    485         return equal(a.characters16(), b.characters8(), aLength);
    486 
    487     return equal(a.characters16(), b.characters16(), aLength);
     478    return equalCommon(a, b);
    488479}
    489480
  • trunk/Source/WTF/wtf/text/WTFString.h

    r181512 r181525  
    264264    bool startsWith(const String& s) const
    265265        { return m_impl ? m_impl->startsWith(s.impl()) : s.isEmpty(); }
     266    bool startsWithIgnoringASCIICase(const String& s) const
     267        { return m_impl ? m_impl->startsWithIgnoringASCIICase(s.impl()) : s.isEmpty(); }
    266268    bool startsWith(const String& s, bool caseSensitive) const
    267269        { return m_impl ? m_impl->startsWith(s.impl(), caseSensitive) : s.isEmpty(); }
     
    274276        { return m_impl && prefix.impl() ? m_impl->hasInfixStartingAt(*prefix.impl(), startOffset) : false; }
    275277
    276     bool endsWith(const String& s, bool caseSensitive = true) const
     278    bool endsWith(const String& s) const
     279        { return m_impl ? m_impl->endsWith(s.impl()) : s.isEmpty(); }
     280    bool endsWithIgnoringASCIICase(const String& s) const
     281        { return m_impl ? m_impl->endsWithIgnoringASCIICase(s.impl()) : s.isEmpty(); }
     282    bool endsWith(const String& s, bool caseSensitive) const
    277283        { return m_impl ? m_impl->endsWith(s.impl(), caseSensitive) : s.isEmpty(); }
    278284    bool endsWith(UChar character) const
  • trunk/Source/WebCore/ChangeLog

    r181524 r181525  
     12015-03-15  Benjamin Poulain  <benjamin@webkit.org>
     2
     3        CSS: fix the case-insensitive matching of the attribute selectors Begin, End and Hyphen
     4        https://bugs.webkit.org/show_bug.cgi?id=142715
     5
     6        Reviewed by Brent Fulgham.
     7
     8        Fix attribute matching with:
     9        -Begin: [a^=b].
     10        -End: [a$=b].
     11        -Hyphen: [a|=b].
     12
     13        Tests: fast/selectors/attribute-endswith-value-matching-is-ascii-case-insensitive.html
     14               fast/selectors/attribute-hyphen-value-matching-is-ascii-case-insensitive.html
     15               fast/selectors/attribute-startswith-value-matching-is-ascii-case-insensitive.html
     16
     17        * css/SelectorChecker.cpp:
     18        (WebCore::attributeValueMatches):
     19        I forgot to change CSSSelector::Exact in my last patch.
     20        The tests could not catch that since we use the CSS JIT almost everywhere.
     21
     22        * cssjit/SelectorCompiler.cpp:
     23        (WebCore::SelectorCompiler::attributeValueBeginsWith):
     24        (WebCore::SelectorCompiler::attributeValueEndsWith):
     25        (WebCore::SelectorCompiler::attributeValueMatchHyphenRule):
     26
    1272015-03-15  Dan Bernstein  <mitz@apple.com>
    228
  • trunk/Source/WebCore/css/SelectorChecker.cpp

    r181197 r181525  
    421421        break;
    422422    case CSSSelector::Exact:
    423         if (caseSensitive ? selectorValue != value : !equalIgnoringCase(selectorValue, value))
     423        if (caseSensitive ? selectorValue != value : !equalIgnoringASCIICase(selectorValue, value))
    424424            return false;
    425425        break;
     
    451451        break;
    452452    case CSSSelector::Begin:
    453         if (!value.startsWith(selectorValue, caseSensitive) || selectorValue.isEmpty())
    454             return false;
     453        if (selectorValue.isEmpty())
     454            return false;
     455        if (caseSensitive) {
     456            if (!value.startsWith(selectorValue))
     457                return false;
     458        } else {
     459            if (!value.startsWithIgnoringASCIICase(selectorValue))
     460                return false;
     461        }
    455462        break;
    456463    case CSSSelector::End:
    457         if (!value.endsWith(selectorValue, caseSensitive) || selectorValue.isEmpty())
    458             return false;
     464        if (selectorValue.isEmpty())
     465            return false;
     466        if (caseSensitive) {
     467            if (!value.endsWith(selectorValue))
     468                return false;
     469        } else {
     470            if (!value.endsWithIgnoringASCIICase(selectorValue))
     471                return false;
     472        }
    459473        break;
    460474    case CSSSelector::Hyphen:
    461475        if (value.length() < selectorValue.length())
    462476            return false;
    463         if (!value.startsWith(selectorValue, caseSensitive))
    464             return false;
     477        if (caseSensitive) {
     478            if (!value.startsWith(selectorValue))
     479                return false;
     480        } else {
     481            if (!value.startsWithIgnoringASCIICase(selectorValue))
     482                return false;
     483        }
    465484        // It they start the same, check for exact match or following '-':
    466485        if (value.length() != selectorValue.length() && value[selectorValue.length()] != '-')
  • trunk/Source/WebCore/cssjit/SelectorCompiler.cpp

    r181512 r181525  
    28042804static bool attributeValueBeginsWith(const Attribute* attribute, AtomicStringImpl* expectedString)
    28052805{
     2806    ASSERT(expectedString);
     2807
    28062808    AtomicStringImpl& valueImpl = *attribute->value().impl();
    28072809    if (caseSensitivity == CaseSensitive)
    2808         return valueImpl.startsWith(expectedString);
    2809     return valueImpl.startsWith(expectedString, false);
     2810        return valueImpl.startsWith(*expectedString);
     2811    return valueImpl.startsWithIgnoringASCIICase(*expectedString);
    28102812}
    28112813
     
    28222824static bool attributeValueEndsWith(const Attribute* attribute, AtomicStringImpl* expectedString)
    28232825{
     2826    ASSERT(expectedString);
     2827
    28242828    AtomicStringImpl& valueImpl = *attribute->value().impl();
    28252829    if (caseSensitivity == CaseSensitive)
    2826         return valueImpl.endsWith(expectedString);
    2827     return valueImpl.endsWith(expectedString, false);
     2830        return valueImpl.endsWith(*expectedString);
     2831    return valueImpl.endsWithIgnoringASCIICase(*expectedString);
    28282832}
    28292833
     
    28312835static bool attributeValueMatchHyphenRule(const Attribute* attribute, AtomicStringImpl* expectedString)
    28322836{
     2837    ASSERT(expectedString);
     2838
    28332839    AtomicStringImpl& valueImpl = *attribute->value().impl();
    28342840    if (valueImpl.length() < expectedString->length())
     
    28372843    bool valueStartsWithExpectedString;
    28382844    if (caseSensitivity == CaseSensitive)
    2839         valueStartsWithExpectedString = valueImpl.startsWith(expectedString);
     2845        valueStartsWithExpectedString = valueImpl.startsWith(*expectedString);
    28402846    else
    2841         valueStartsWithExpectedString = valueImpl.startsWith(expectedString, false);
     2847        valueStartsWithExpectedString = valueImpl.startsWithIgnoringASCIICase(*expectedString);
    28422848
    28432849    if (!valueStartsWithExpectedString)
  • trunk/Tools/ChangeLog

    r181512 r181525  
     12015-03-15  Benjamin Poulain  <benjamin@webkit.org>
     2
     3        CSS: fix the case-insensitive matching of the attribute selectors Begin, End and Hyphen
     4        https://bugs.webkit.org/show_bug.cgi?id=142715
     5
     6        Reviewed by Brent Fulgham.
     7
     8        * TestWebKitAPI/Tests/WTF/StringImpl.cpp:
     9        (TestWebKitAPI::TEST):
     10
    1112015-03-15  Benjamin Poulain  <bpoulain@apple.com>
    212
  • trunk/Tools/TestWebKitAPI/Tests/WTF/StringImpl.cpp

    r181512 r181525  
    165165}
    166166
     167TEST(WTF, StringImplStartsWithIgnoringASCIICaseBasic)
     168{
     169    RefPtr<StringImpl> reference = StringImpl::create(reinterpret_cast<const LChar*>("aBcéX"));
     170    RefPtr<StringImpl> referenceEquivalent = StringImpl::create(reinterpret_cast<const LChar*>("AbCéx"));
     171
     172    // Identity.
     173    ASSERT_TRUE(reference->startsWithIgnoringASCIICase(reference.get()));
     174    ASSERT_TRUE(reference->startsWithIgnoringASCIICase(*reference.get()));
     175    ASSERT_TRUE(reference->startsWithIgnoringASCIICase(referenceEquivalent.get()));
     176    ASSERT_TRUE(reference->startsWithIgnoringASCIICase(*referenceEquivalent.get()));
     177    ASSERT_TRUE(referenceEquivalent->startsWithIgnoringASCIICase(reference.get()));
     178    ASSERT_TRUE(referenceEquivalent->startsWithIgnoringASCIICase(*reference.get()));
     179    ASSERT_TRUE(referenceEquivalent->startsWithIgnoringASCIICase(referenceEquivalent.get()));
     180    ASSERT_TRUE(referenceEquivalent->startsWithIgnoringASCIICase(*referenceEquivalent.get()));
     181
     182    // Proper prefixes.
     183    RefPtr<StringImpl> aLower = StringImpl::createFromLiteral("a");
     184    ASSERT_TRUE(reference->startsWithIgnoringASCIICase(aLower.get()));
     185    ASSERT_TRUE(reference->startsWithIgnoringASCIICase(*aLower.get()));
     186    RefPtr<StringImpl> aUpper = StringImpl::createFromLiteral("A");
     187    ASSERT_TRUE(reference->startsWithIgnoringASCIICase(aUpper.get()));
     188    ASSERT_TRUE(reference->startsWithIgnoringASCIICase(*aUpper.get()));
     189
     190    RefPtr<StringImpl> abcLower = StringImpl::createFromLiteral("abc");
     191    ASSERT_TRUE(reference->startsWithIgnoringASCIICase(abcLower.get()));
     192    ASSERT_TRUE(reference->startsWithIgnoringASCIICase(*abcLower.get()));
     193    RefPtr<StringImpl> abcUpper = StringImpl::createFromLiteral("ABC");
     194    ASSERT_TRUE(reference->startsWithIgnoringASCIICase(abcUpper.get()));
     195    ASSERT_TRUE(reference->startsWithIgnoringASCIICase(*abcUpper.get()));
     196
     197    RefPtr<StringImpl> abcAccentLower = StringImpl::create(reinterpret_cast<const LChar*>("abcé"));
     198    ASSERT_TRUE(reference->startsWithIgnoringASCIICase(abcAccentLower.get()));
     199    ASSERT_TRUE(reference->startsWithIgnoringASCIICase(*abcAccentLower.get()));
     200    RefPtr<StringImpl> abcAccentUpper = StringImpl::create(reinterpret_cast<const LChar*>("ABCé"));
     201    ASSERT_TRUE(reference->startsWithIgnoringASCIICase(abcAccentUpper.get()));
     202    ASSERT_TRUE(reference->startsWithIgnoringASCIICase(*abcAccentUpper.get()));
     203
     204    // Negative cases.
     205    RefPtr<StringImpl> differentFirstChar = StringImpl::create(reinterpret_cast<const LChar*>("bBcéX"));
     206    RefPtr<StringImpl> differentFirstCharProperPrefix = StringImpl::create(reinterpret_cast<const LChar*>("CBcé"));
     207    ASSERT_FALSE(reference->startsWithIgnoringASCIICase(differentFirstChar.get()));
     208    ASSERT_FALSE(reference->startsWithIgnoringASCIICase(*differentFirstChar.get()));
     209    ASSERT_FALSE(reference->startsWithIgnoringASCIICase(differentFirstCharProperPrefix.get()));
     210    ASSERT_FALSE(reference->startsWithIgnoringASCIICase(*differentFirstCharProperPrefix.get()));
     211
     212    RefPtr<StringImpl> uppercaseAccent = StringImpl::create(reinterpret_cast<const LChar*>("aBcÉX"));
     213    RefPtr<StringImpl> uppercaseAccentProperPrefix = StringImpl::create(reinterpret_cast<const LChar*>("aBcÉX"));
     214    ASSERT_FALSE(reference->startsWithIgnoringASCIICase(uppercaseAccent.get()));
     215    ASSERT_FALSE(reference->startsWithIgnoringASCIICase(*uppercaseAccent.get()));
     216    ASSERT_FALSE(reference->startsWithIgnoringASCIICase(uppercaseAccentProperPrefix.get()));
     217    ASSERT_FALSE(reference->startsWithIgnoringASCIICase(*uppercaseAccentProperPrefix.get()));
     218}
     219
     220TEST(WTF, StringImplStartsWithIgnoringASCIICaseWithNull)
     221{
     222    RefPtr<StringImpl> reference = StringImpl::createFromLiteral("aBcDeFG");
     223    ASSERT_FALSE(reference->startsWithIgnoringASCIICase(nullptr));
     224
     225    RefPtr<StringImpl> empty = StringImpl::create(reinterpret_cast<const LChar*>(""));
     226    ASSERT_FALSE(empty->startsWithIgnoringASCIICase(nullptr));
     227}
     228
     229TEST(WTF, StringImplStartsWithIgnoringASCIICaseWithEmpty)
     230{
     231    RefPtr<StringImpl> reference = StringImpl::createFromLiteral("aBcDeFG");
     232    RefPtr<StringImpl> empty = StringImpl::create(reinterpret_cast<const LChar*>(""));
     233    ASSERT_TRUE(reference->startsWithIgnoringASCIICase(empty.get()));
     234    ASSERT_TRUE(reference->startsWithIgnoringASCIICase(*empty.get()));
     235    ASSERT_TRUE(empty->startsWithIgnoringASCIICase(empty.get()));
     236    ASSERT_TRUE(empty->startsWithIgnoringASCIICase(*empty.get()));
     237    ASSERT_FALSE(empty->startsWithIgnoringASCIICase(reference.get()));
     238    ASSERT_FALSE(empty->startsWithIgnoringASCIICase(*reference.get()));
     239}
     240
     241TEST(WTF, StringImplEndsWithIgnoringASCIICaseBasic)
     242{
     243    RefPtr<StringImpl> reference = StringImpl::create(reinterpret_cast<const LChar*>("XÉCbA"));
     244    RefPtr<StringImpl> referenceEquivalent = StringImpl::create(reinterpret_cast<const LChar*>("xÉcBa"));
     245
     246    // Identity.
     247    ASSERT_TRUE(reference->endsWithIgnoringASCIICase(reference.get()));
     248    ASSERT_TRUE(reference->endsWithIgnoringASCIICase(*reference.get()));
     249    ASSERT_TRUE(reference->endsWithIgnoringASCIICase(referenceEquivalent.get()));
     250    ASSERT_TRUE(reference->endsWithIgnoringASCIICase(*referenceEquivalent.get()));
     251    ASSERT_TRUE(referenceEquivalent->endsWithIgnoringASCIICase(reference.get()));
     252    ASSERT_TRUE(referenceEquivalent->endsWithIgnoringASCIICase(*reference.get()));
     253    ASSERT_TRUE(referenceEquivalent->endsWithIgnoringASCIICase(referenceEquivalent.get()));
     254    ASSERT_TRUE(referenceEquivalent->endsWithIgnoringASCIICase(*referenceEquivalent.get()));
     255
     256    // Proper suffixes.
     257    RefPtr<StringImpl> aLower = StringImpl::createFromLiteral("a");
     258    ASSERT_TRUE(reference->endsWithIgnoringASCIICase(aLower.get()));
     259    ASSERT_TRUE(reference->endsWithIgnoringASCIICase(*aLower.get()));
     260    RefPtr<StringImpl> aUpper = StringImpl::createFromLiteral("a");
     261    ASSERT_TRUE(reference->endsWithIgnoringASCIICase(aUpper.get()));
     262    ASSERT_TRUE(reference->endsWithIgnoringASCIICase(*aUpper.get()));
     263
     264    RefPtr<StringImpl> abcLower = StringImpl::createFromLiteral("cba");
     265    ASSERT_TRUE(reference->endsWithIgnoringASCIICase(abcLower.get()));
     266    ASSERT_TRUE(reference->endsWithIgnoringASCIICase(*abcLower.get()));
     267    RefPtr<StringImpl> abcUpper = StringImpl::createFromLiteral("CBA");
     268    ASSERT_TRUE(reference->endsWithIgnoringASCIICase(abcUpper.get()));
     269    ASSERT_TRUE(reference->endsWithIgnoringASCIICase(*abcUpper.get()));
     270
     271    RefPtr<StringImpl> abcAccentLower = StringImpl::create(reinterpret_cast<const LChar*>("Écba"));
     272    ASSERT_TRUE(reference->endsWithIgnoringASCIICase(abcAccentLower.get()));
     273    ASSERT_TRUE(reference->endsWithIgnoringASCIICase(*abcAccentLower.get()));
     274    RefPtr<StringImpl> abcAccentUpper = StringImpl::create(reinterpret_cast<const LChar*>("ÉCBA"));
     275    ASSERT_TRUE(reference->endsWithIgnoringASCIICase(abcAccentUpper.get()));
     276    ASSERT_TRUE(reference->endsWithIgnoringASCIICase(*abcAccentUpper.get()));
     277
     278    // Negative cases.
     279    RefPtr<StringImpl> differentLastChar = StringImpl::create(reinterpret_cast<const LChar*>("XÉCbB"));
     280    RefPtr<StringImpl> differentLastCharProperSuffix = StringImpl::create(reinterpret_cast<const LChar*>("ÉCbb"));
     281    ASSERT_FALSE(reference->endsWithIgnoringASCIICase(differentLastChar.get()));
     282    ASSERT_FALSE(reference->endsWithIgnoringASCIICase(*differentLastChar.get()));
     283    ASSERT_FALSE(reference->endsWithIgnoringASCIICase(differentLastCharProperSuffix.get()));
     284    ASSERT_FALSE(reference->endsWithIgnoringASCIICase(*differentLastCharProperSuffix.get()));
     285
     286    RefPtr<StringImpl> lowercaseAccent = StringImpl::create(reinterpret_cast<const LChar*>("aBcéX"));
     287    RefPtr<StringImpl> loweraseAccentProperSuffix = StringImpl::create(reinterpret_cast<const LChar*>("aBcéX"));
     288    ASSERT_FALSE(reference->endsWithIgnoringASCIICase(lowercaseAccent.get()));
     289    ASSERT_FALSE(reference->endsWithIgnoringASCIICase(*lowercaseAccent.get()));
     290    ASSERT_FALSE(reference->endsWithIgnoringASCIICase(loweraseAccentProperSuffix.get()));
     291    ASSERT_FALSE(reference->endsWithIgnoringASCIICase(*loweraseAccentProperSuffix.get()));
     292}
     293
     294TEST(WTF, StringImplEndsWithIgnoringASCIICaseWithNull)
     295{
     296    RefPtr<StringImpl> reference = StringImpl::createFromLiteral("aBcDeFG");
     297    ASSERT_FALSE(reference->endsWithIgnoringASCIICase(nullptr));
     298
     299    RefPtr<StringImpl> empty = StringImpl::create(reinterpret_cast<const LChar*>(""));
     300    ASSERT_FALSE(empty->endsWithIgnoringASCIICase(nullptr));
     301}
     302
     303TEST(WTF, StringImplEndsWithIgnoringASCIICaseWithEmpty)
     304{
     305    RefPtr<StringImpl> reference = StringImpl::createFromLiteral("aBcDeFG");
     306    RefPtr<StringImpl> empty = StringImpl::create(reinterpret_cast<const LChar*>(""));
     307    ASSERT_TRUE(reference->endsWithIgnoringASCIICase(empty.get()));
     308    ASSERT_TRUE(reference->endsWithIgnoringASCIICase(*empty.get()));
     309    ASSERT_TRUE(empty->endsWithIgnoringASCIICase(empty.get()));
     310    ASSERT_TRUE(empty->endsWithIgnoringASCIICase(*empty.get()));
     311    ASSERT_FALSE(empty->endsWithIgnoringASCIICase(reference.get()));
     312    ASSERT_FALSE(empty->endsWithIgnoringASCIICase(*reference.get()));
     313}
     314
    167315} // namespace TestWebKitAPI
Note: See TracChangeset for help on using the changeset viewer.