Example #1
0
CSSSupportsParser::SupportsResult CSSSupportsParser::consumeNegation(
    CSSParserTokenRange range) {
  ASSERT(range.peek().type() == IdentToken);
  if (!equalIgnoringASCIICase(range.consume().value(), "not"))
    return Invalid;
  if (range.consumeIncludingWhitespace().type() != WhitespaceToken)
    return Invalid;
  SupportsResult result = consumeConditionInParenthesis(range);
  range.consumeWhitespace();
  if (!range.atEnd() || result == Invalid)
    return Invalid;
  return result ? Unsupported : Supported;
}
bool UserContentURLPattern::matches(const URL& test) const
{
    if (m_invalid)
        return false;

    if (!equalIgnoringASCIICase(test.protocol(), m_scheme))
        return false;

    if (!equalLettersIgnoringASCIICase(m_scheme, "file") && !matchesHost(test))
        return false;

    return matchesPath(test);
}
Example #3
0
String XMLHttpRequest::uppercaseKnownHTTPMethod(const String& method)
{
    const char* const methods[] = { "DELETE", "GET", "HEAD", "OPTIONS", "POST", "PUT" };
    for (auto* value : methods) {
        if (equalIgnoringASCIICase(method, value)) {
            // Don't bother allocating a new string if it's already all uppercase.
            if (method == value)
                break;
            return ASCIILiteral(value);
        }
    }
    return method;
}
Example #4
0
static String canonicalizeTimeZoneName(const String& timeZoneName)
{
    // 6.4.1 IsValidTimeZoneName (timeZone)
    // The abstract operation returns true if timeZone, converted to upper case as described in 6.1, is equal to one of the Zone or Link names of the IANA Time Zone Database, converted to upper case as described in 6.1. It returns false otherwise.
    UErrorCode status = U_ZERO_ERROR;
    UEnumeration* timeZones = ucal_openTimeZones(&status);
    ASSERT(U_SUCCESS(status));

    String canonical;
    do {
        status = U_ZERO_ERROR;
        int32_t ianaTimeZoneLength;
        // Time zone names are respresented as UChar[] in all related ICU apis.
        const UChar* ianaTimeZone = uenum_unext(timeZones, &ianaTimeZoneLength, &status);
        ASSERT(U_SUCCESS(status));

        // End of enumeration.
        if (!ianaTimeZone)
            break;

        StringView ianaTimeZoneView(ianaTimeZone, ianaTimeZoneLength);
        if (!equalIgnoringASCIICase(timeZoneName, ianaTimeZoneView))
            continue;

        // Found a match, now canonicalize.
        // 6.4.2 CanonicalizeTimeZoneName (timeZone) (ECMA-402 2.0)
        // 1. Let ianaTimeZone be the Zone or Link name of the IANA Time Zone Database such that timeZone, converted to upper case as described in 6.1, is equal to ianaTimeZone, converted to upper case as described in 6.1.
        // 2. If ianaTimeZone is a Link name, then let ianaTimeZone be the corresponding Zone name as specified in the “backward” file of the IANA Time Zone Database.

        Vector<UChar, 32> buffer(ianaTimeZoneLength);
        status = U_ZERO_ERROR;
        auto canonicalLength = ucal_getCanonicalTimeZoneID(ianaTimeZone, ianaTimeZoneLength, buffer.data(), ianaTimeZoneLength, nullptr, &status);
        if (status == U_BUFFER_OVERFLOW_ERROR) {
            buffer.grow(canonicalLength);
            status = U_ZERO_ERROR;
            ucal_getCanonicalTimeZoneID(ianaTimeZone, ianaTimeZoneLength, buffer.data(), canonicalLength, nullptr, &status);
        }
        ASSERT(U_SUCCESS(status));
        canonical = String(buffer.data(), canonicalLength);
    } while (canonical.isNull());
    uenum_close(timeZones);

    // 3. If ianaTimeZone is "Etc/UTC" or "Etc/GMT", then return "UTC".
    if (canonical == "Etc/UTC" || canonical == "Etc/GMT")
        canonical = ASCIILiteral("UTC");

    // 4. Return ianaTimeZone.
    return canonical;
}
int DatabaseAuthorizer::denyBasedOnTableName(const String& tableName) const
{
    if (!m_securityEnabled)
        return SQLAuthAllow;

    // Sadly, normal creates and drops end up affecting sqlite_master in an authorizer callback, so
    // it will be tough to enforce all of the following policies.
    // if (equalIgnoringASCIICase(tableName, "sqlite_master")
    //      || equalIgnoringASCIICase(tableName, "sqlite_temp_master")
    //      || equalIgnoringASCIICase(tableName, "sqlite_sequence")
    //      || equalIgnoringASCIICase(tableName, Database::databaseInfoTableName()))
    //    return SQLAuthDeny;

    if (equalIgnoringASCIICase(tableName, m_databaseInfoTableName))
        return SQLAuthDeny;

    return SQLAuthAllow;
}
Example #6
0
bool SecurityOrigin::canDisplay(const URL& url) const
{
    if (m_universalAccess)
        return true;

    if (isFeedWithNestedProtocolInHTTPFamily(url))
        return true;

    String protocol = url.protocol().toString();

    if (SchemeRegistry::canDisplayOnlyIfCanRequest(protocol))
        return canRequest(url);

    if (SchemeRegistry::shouldTreatURLSchemeAsDisplayIsolated(protocol))
        return equalIgnoringASCIICase(m_protocol, protocol) || SecurityPolicy::isAccessToURLWhiteListed(this, url);

    if (SecurityPolicy::restrictAccessToLocal() && SchemeRegistry::shouldTreatURLSchemeAsLocal(protocol))
        return canLoadLocalResources() || SecurityPolicy::isAccessToURLWhiteListed(this, url);

    return true;
}
bool UserContentURLPattern::matchesHost(const URL& test) const
{
    const String& host = test.host();
    if (equalIgnoringASCIICase(host, m_host))
        return true;

    if (!m_matchSubdomains)
        return false;

    // If we're matching subdomains, and we have no host, that means the pattern
    // was <scheme>://*/<whatever>, so we match anything.
    if (!m_host.length())
        return true;

    // Check if the domain is a subdomain of our host.
    if (!host.endsWith(m_host, false))
        return false;

    ASSERT(host.length() > m_host.length());

    // Check that the character before the suffix is a period.
    return host[host.length() - m_host.length() - 1] == '.';
}
Example #8
0
static bool passesTimingAllowCheck(
    const ResourceResponse& response,
    const SecurityOrigin& initiatorSecurityOrigin,
    const AtomicString& originalTimingAllowOrigin,
    ExecutionContext* context) {
  RefPtr<SecurityOrigin> resourceOrigin =
      SecurityOrigin::create(response.url());
  if (resourceOrigin->isSameSchemeHostPort(&initiatorSecurityOrigin))
    return true;

  const AtomicString& timingAllowOriginString =
      originalTimingAllowOrigin.isEmpty()
          ? response.httpHeaderField(HTTPNames::Timing_Allow_Origin)
          : originalTimingAllowOrigin;
  if (timingAllowOriginString.isEmpty() ||
      equalIgnoringASCIICase(timingAllowOriginString, "null"))
    return false;

  if (timingAllowOriginString == "*") {
    UseCounter::count(context, UseCounter::StarInTimingAllowOrigin);
    return true;
  }

  const String& securityOrigin = initiatorSecurityOrigin.toString();
  Vector<String> timingAllowOrigins;
  timingAllowOriginString.getString().split(' ', timingAllowOrigins);
  if (timingAllowOrigins.size() > 1)
    UseCounter::count(context, UseCounter::MultipleOriginsInTimingAllowOrigin);
  else if (timingAllowOrigins.size() == 1)
    UseCounter::count(context, UseCounter::SingleOriginInTimingAllowOrigin);
  for (const String& allowOrigin : timingAllowOrigins) {
    if (allowOrigin == securityOrigin)
      return true;
  }

  return false;
}
void RadioButtonGroupScope::addButton(HTMLInputElement* element) {
  DCHECK_EQ(element->type(), InputTypeNames::radio);
  if (element->name().isEmpty())
    return;

  if (!m_nameToGroupMap)
    m_nameToGroupMap = new NameToGroupMap;

  auto keyValue = m_nameToGroupMap->add(element->name(), nullptr).storedValue;
  if (!keyValue->value) {
    keyValue->value = RadioButtonGroup::create();
  } else {
    if (keyValue->key == element->name())
      UseCounter::count(element->document(),
                        UseCounter::RadioNameMatchingStrict);
    else if (equalIgnoringASCIICase(keyValue->key, element->name()))
      UseCounter::count(element->document(),
                        UseCounter::RadioNameMatchingASCIICaseless);
    else
      UseCounter::count(element->document(),
                        UseCounter::RadioNameMatchingCaseFolding);
  }
  keyValue->value->add(element);
}
bool ApplicationCacheHost::shouldLoadResourceFromApplicationCache(const ResourceRequest& request, ApplicationCacheResource*& resource)
{
    ApplicationCache* cache = applicationCache();
    if (!cache || !cache->isComplete())
        return false;

    // If the resource is not to be fetched using the HTTP GET mechanism or equivalent, or if its URL has a different
    // <scheme> component than the application cache's manifest, then fetch the resource normally.
    if (!ApplicationCache::requestIsHTTPOrHTTPSGet(request) || !equalIgnoringASCIICase(request.url().protocol(), cache->manifestResource()->url().protocol()))
        return false;

    // If the resource's URL is an master entry, the manifest, an explicit entry, or a fallback entry
    // in the application cache, then get the resource from the cache (instead of fetching it).
    resource = cache->resourceForURL(request.url());

    // Resources that match fallback namespaces or online whitelist entries are fetched from the network,
    // unless they are also cached.
    if (!resource && (cache->allowsAllNetworkRequests() || cache->urlMatchesFallbackNamespace(request.url()) || cache->isURLInOnlineWhitelist(request.url())))
        return false;

    // Resources that are not present in the manifest will always fail to load (at least, after the
    // cache has been primed the first time), making the testing of offline applications simpler.
    return true;
}
std::unique_ptr<FontPlatformData> FontCache::createFontPlatformData(const FontDescription& fontDescription, const AtomicString& family, const FontFeatureSettings*, const FontVariantSettings*)
{
    // The CSS font matching algorithm (http://www.w3.org/TR/css3-fonts/#font-matching-algorithm)
    // says that we must find an exact match for font family, slant (italic or oblique can be used)
    // and font weight (we only match bold/non-bold here).
    RefPtr<FcPattern> pattern = adoptRef(FcPatternCreate());
    // Never choose unscalable fonts, as they pixelate when displayed at different sizes.
    FcPatternAddBool(pattern.get(), FC_SCALABLE, FcTrue);
    String familyNameString(getFamilyNameStringFromFamily(family));
    if (!FcPatternAddString(pattern.get(), FC_FAMILY, reinterpret_cast<const FcChar8*>(familyNameString.utf8().data())))
        return nullptr;

    bool italic = fontDescription.italic();
    if (!FcPatternAddInteger(pattern.get(), FC_SLANT, italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN))
        return nullptr;
    if (!FcPatternAddInteger(pattern.get(), FC_WEIGHT, fontWeightToFontconfigWeight(fontDescription.weight())))
        return nullptr;
    if (!FcPatternAddDouble(pattern.get(), FC_PIXEL_SIZE, fontDescription.computedPixelSize()))
        return nullptr;

    // The strategy is originally from Skia (src/ports/SkFontHost_fontconfig.cpp):
    //
    // We do not normally allow fontconfig to substitute one font family for another, since this
    // would break CSS font family fallback: the website should be in control of fallback. During
    // normal font matching, the only font family substitution permitted is for generic families
    // (sans, serif, monospace) or for strongly-aliased fonts (which are to be treated as
    // effectively identical). This is because the font matching step is designed to always find a
    // match for the font, which we don't want.
    //
    // Fontconfig is used in two stages: (1) configuration and (2) matching. During the
    // configuration step, before any matching occurs, we allow arbitrary family substitutions,
    // since this is an exact matter of respecting the user's font configuration.
    FcConfigSubstitute(nullptr, pattern.get(), FcMatchPattern);
    FcDefaultSubstitute(pattern.get());

    FcChar8* fontConfigFamilyNameAfterConfiguration;
    FcPatternGetString(pattern.get(), FC_FAMILY, 0, &fontConfigFamilyNameAfterConfiguration);
    String familyNameAfterConfiguration = String::fromUTF8(reinterpret_cast<char*>(fontConfigFamilyNameAfterConfiguration));

    FcResult fontConfigResult;
    RefPtr<FcPattern> resultPattern = adoptRef(FcFontMatch(nullptr, pattern.get(), &fontConfigResult));
    if (!resultPattern) // No match.
        return nullptr;

    FcChar8* fontConfigFamilyNameAfterMatching;
    FcPatternGetString(resultPattern.get(), FC_FAMILY, 0, &fontConfigFamilyNameAfterMatching);
    String familyNameAfterMatching = String::fromUTF8(reinterpret_cast<char*>(fontConfigFamilyNameAfterMatching));

    // If Fontconfig gave us a different font family than the one we requested, we should ignore it
    // and allow WebCore to give us the next font on the CSS fallback list. The exceptions are if
    // this family name is a commonly-used generic family, or if the families are strongly-aliased.
    // Checking for a strong alias comes last, since it is slow.
    if (!equalIgnoringASCIICase(familyNameAfterConfiguration, familyNameAfterMatching) && !isCommonlyUsedGenericFamily(familyNameString) && !areStronglyAliased(familyNameAfterConfiguration, familyNameAfterMatching))
        return nullptr;

    // Verify that this font has an encoding compatible with Fontconfig. Fontconfig currently
    // supports three encodings in FcFreeTypeCharIndex: Unicode, Symbol and AppleRoman.
    // If this font doesn't have one of these three encodings, don't select it.
    auto platformData = std::make_unique<FontPlatformData>(resultPattern.get(), fontDescription);
    if (!platformData->hasCompatibleCharmap())
        return nullptr;

    return platformData;
}
bool parseManifest(const URL& manifestURL, const char* data, int length, Manifest& manifest)
{
    ASSERT(manifest.explicitURLs.isEmpty());
    ASSERT(manifest.onlineWhitelistedURLs.isEmpty());
    ASSERT(manifest.fallbackURLs.isEmpty());
    manifest.allowAllNetworkRequests = false;

    Mode mode = Explicit;

    String s = TextResourceDecoder::create("text/cache-manifest", "UTF-8")->decodeAndFlush(data, length);
    
    // Look for the magic signature: "^\xFEFF?CACHE MANIFEST[ \t]?" (the BOM is removed by TextResourceDecoder).
    // Example: "CACHE MANIFEST #comment" is a valid signature.
    // Example: "CACHE MANIFEST;V2" is not.
    if (!s.startsWith("CACHE MANIFEST"))
        return false;
    
    StringView manifestAfterSignature = StringView(s).substring(14); // "CACHE MANIFEST" is 14 characters.
    auto upconvertedCharacters = manifestAfterSignature.upconvertedCharacters();
    const UChar* p = upconvertedCharacters;
    const UChar* end = p + manifestAfterSignature.length();

    if (p < end && *p != ' ' && *p != '\t' && *p != '\n' && *p != '\r')
        return false;

    // Skip to the end of the line.
    while (p < end && *p != '\r' && *p != '\n')
        p++;

    while (1) {
        // Skip whitespace
        while (p < end && (*p == '\n' || *p == '\r' || *p == ' ' || *p == '\t'))
            p++;
        
        if (p == end)
            break;
        
        const UChar* lineStart = p;
        
        // Find the end of the line
        while (p < end && *p != '\r' && *p != '\n')
            p++;
        
        // Check if we have a comment
        if (*lineStart == '#')
            continue;
        
        // Get rid of trailing whitespace
        const UChar* tmp = p - 1;
        while (tmp > lineStart && (*tmp == ' ' || *tmp == '\t'))
            tmp--;
        
        String line(lineStart, tmp - lineStart + 1);

        if (line == "CACHE:") 
            mode = Explicit;
        else if (line == "FALLBACK:")
            mode = Fallback;
        else if (line == "NETWORK:")
            mode = OnlineWhitelist;
        else if (line.endsWith(':'))
            mode = Unknown;
        else if (mode == Unknown)
            continue;
        else if (mode == Explicit || mode == OnlineWhitelist) {
            auto upconvertedLineCharacters = StringView(line).upconvertedCharacters();
            const UChar* p = upconvertedLineCharacters;
            const UChar* lineEnd = p + line.length();
            
            // Look for whitespace separating the URL from subsequent ignored tokens.
            while (p < lineEnd && *p != '\t' && *p != ' ') 
                p++;

            if (mode == OnlineWhitelist && p - upconvertedLineCharacters == 1 && line[0] == '*') {
                // Wildcard was found.
                manifest.allowAllNetworkRequests = true;
                continue;
            }

            URL url(manifestURL, line.substring(0, p - upconvertedLineCharacters));
            
            if (!url.isValid())
                continue;

            if (url.hasFragmentIdentifier())
                url.removeFragmentIdentifier();
            
            if (!equalIgnoringASCIICase(url.protocol(), manifestURL.protocol()))
                continue;
            
            if (mode == Explicit && manifestURL.protocolIs("https") && !protocolHostAndPortAreEqual(manifestURL, url))
                continue;
            
            if (mode == Explicit)
                manifest.explicitURLs.add(url.string());
            else
                manifest.onlineWhitelistedURLs.append(url);
            
        } else if (mode == Fallback) {
            auto upconvertedLineCharacters = StringView(line).upconvertedCharacters();
            const UChar* p = upconvertedLineCharacters;
            const UChar* lineEnd = p + line.length();
            
            // Look for whitespace separating the two URLs
            while (p < lineEnd && *p != '\t' && *p != ' ') 
                p++;

            if (p == lineEnd) {
                // There was no whitespace separating the URLs.
                continue;
            }
            
            URL namespaceURL(manifestURL, line.substring(0, p - upconvertedLineCharacters));
            if (!namespaceURL.isValid())
                continue;
            if (namespaceURL.hasFragmentIdentifier())
                namespaceURL.removeFragmentIdentifier();

            if (!protocolHostAndPortAreEqual(manifestURL, namespaceURL))
                continue;
                                   
            // Skip whitespace separating fallback namespace from URL.
            while (p < lineEnd && (*p == '\t' || *p == ' '))
                p++;

            // Look for whitespace separating the URL from subsequent ignored tokens.
            const UChar* fallbackStart = p;
            while (p < lineEnd && *p != '\t' && *p != ' ') 
                p++;

            URL fallbackURL(manifestURL, String(fallbackStart, p - fallbackStart));
            if (!fallbackURL.isValid())
                continue;
            if (fallbackURL.hasFragmentIdentifier())
                fallbackURL.removeFragmentIdentifier();

            if (!protocolHostAndPortAreEqual(manifestURL, fallbackURL))
                continue;

            manifest.fallbackURLs.append(std::make_pair(namespaceURL, fallbackURL));            
        } else 
            ASSERT_NOT_REACHED();
    }

    return true;
}
Example #13
0
static LCID LCIDFromLocaleInternal(LCID userDefaultLCID, const String& userDefaultLanguageCode, String& locale)
{
    if (equalIgnoringASCIICase(extractLanguageCode(locale), userDefaultLanguageCode))
        return userDefaultLCID;
    return LocaleNameToLCID(locale.charactersWithNullTermination().data(), 0);
}
Example #14
0
bool CSSSelectorParser::consumeANPlusB(CSSParserTokenRange& range,
                                       std::pair<int, int>& result) {
  const CSSParserToken& token = range.consume();
  if (token.type() == NumberToken &&
      token.numericValueType() == IntegerValueType) {
    result = std::make_pair(0, static_cast<int>(token.numericValue()));
    return true;
  }
  if (token.type() == IdentToken) {
    if (equalIgnoringASCIICase(token.value(), "odd")) {
      result = std::make_pair(2, 1);
      return true;
    }
    if (equalIgnoringASCIICase(token.value(), "even")) {
      result = std::make_pair(2, 0);
      return true;
    }
  }

  // The 'n' will end up as part of an ident or dimension. For a valid <an+b>,
  // this will store a string of the form 'n', 'n-', or 'n-123'.
  String nString;

  if (token.type() == DelimiterToken && token.delimiter() == '+' &&
      range.peek().type() == IdentToken) {
    result.first = 1;
    nString = range.consume().value().toString();
  } else if (token.type() == DimensionToken &&
             token.numericValueType() == IntegerValueType) {
    result.first = token.numericValue();
    nString = token.value().toString();
  } else if (token.type() == IdentToken) {
    if (token.value()[0] == '-') {
      result.first = -1;
      nString = token.value().toString().substring(1);
    } else {
      result.first = 1;
      nString = token.value().toString();
    }
  }

  range.consumeWhitespace();

  if (nString.isEmpty() || !isASCIIAlphaCaselessEqual(nString[0], 'n'))
    return false;
  if (nString.length() > 1 && nString[1] != '-')
    return false;

  if (nString.length() > 2) {
    bool valid;
    result.second = nString.substring(1).toIntStrict(&valid);
    return valid;
  }

  NumericSign sign = nString.length() == 1 ? NoSign : MinusSign;
  if (sign == NoSign && range.peek().type() == DelimiterToken) {
    char delimiterSign = range.consumeIncludingWhitespace().delimiter();
    if (delimiterSign == '+')
      sign = PlusSign;
    else if (delimiterSign == '-')
      sign = MinusSign;
    else
      return false;
  }

  if (sign == NoSign && range.peek().type() != NumberToken) {
    result.second = 0;
    return true;
  }

  const CSSParserToken& b = range.consume();
  if (b.type() != NumberToken || b.numericValueType() != IntegerValueType)
    return false;
  if ((b.numericSign() == NoSign) == (sign == NoSign))
    return false;
  result.second = b.numericValue();
  if (sign == MinusSign)
    result.second = -result.second;
  return true;
}
static bool isCSPDirectiveName(const String& name)
{
    return equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::baseURI)
        || equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::connectSrc)
        || equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::defaultSrc)
        || equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::fontSrc)
        || equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::formAction)
        || equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::frameSrc)
        || equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::imgSrc)
        || equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::mediaSrc)
        || equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::objectSrc)
        || equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::pluginTypes)
        || equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::reportURI)
        || equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::sandbox)
        || equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::scriptSrc)
        || equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::styleSrc);
}
bool ContentSecurityPolicy::protocolMatchesSelf(const URL& url) const
{
    if (equalLettersIgnoringASCIICase(m_selfSourceProtocol, "http"))
        return url.protocolIsInHTTPFamily();
    return equalIgnoringASCIICase(url.protocol(), m_selfSourceProtocol);
}
Example #17
0
// https://html.spec.whatwg.org/multipage/forms.html#processing-model-3
AutofillData AutofillData::createFromHTMLFormControlElement(const HTMLFormControlElement& element)
{
    static NeverDestroyed<AtomicString> on("on", AtomicString::ConstructFromLiteral);
    static NeverDestroyed<AtomicString> off("off", AtomicString::ConstructFromLiteral);

    // Label: Default
    // 26. Let the element's IDL-exposed autofill value be the empty string, and its autofill hint set and autofill scope be empty.
    // 27. If the element's autocomplete attribute is wearing the autofill anchor mantle, then let the element's autofill field name be the empty string and abort these steps.
    // 28. Let form be the element's form owner, if any, or null otherwise.
    // 29. If form is not null and form's autocomplete attribute is in the off state, then let the element's autofill field name be "off". Otherwise, let the element's autofill field name be "on".
    auto defaultLabel = [&] () -> AutofillData {
        if (element.autofillMantle() == AutofillMantle::Anchor)
            return { emptyString(), emptyString() };
        
        auto form = element.form();
        if (form && form->autocomplete() == off)
            return { off, emptyString() };
        return { on, emptyString() };
    };


    const AtomicString& attributeValue = element.attributeWithoutSynchronization(HTMLNames::autocompleteAttr);

    // 1. If the element has no autocomplete attribute, then jump to the step labeled default.
    if (attributeValue == nullAtom)
        return defaultLabel();

    // 2. Let tokens be the result of splitting the attribute's value on spaces.
    SpaceSplitString tokens(attributeValue, true);

    // 3. If tokens is empty, then jump to the step labeled default.
    if (tokens.isEmpty())
        return defaultLabel();
    
    // 4. Let index be the index of the last token in tokens
    unsigned index = tokens.size() - 1;

    // 5. If the indexth token in tokens is not an ASCII case-insensitive match for one of the
    // tokens given in the first column of the following table, or if the number of tokens in
    // tokens is greater than the maximum number given in the cell in the second column of that
    // token's row, then jump to the step labeled default. Otherwise, let field be the string given
    // in the cell of the first column of the matching row, and let category be the value of the
    // cell in the third column of that same row.
    auto& map = fieldNameMap();

    auto it = map.find(tokens[index]);
    if (it == map.end())
        return defaultLabel();
    
    auto category = it->value.category;

    if (tokens.size() > maxTokensForAutofillFieldCategory(category))
        return defaultLabel();

    const auto& field = tokens[index];

    // 6. If category is Off or Automatic but the element's autocomplete attribute is wearing the
    // autofill anchor mantle, then jump to the step labeled default.
    auto mantle = element.autofillMantle();
    if ((category == AutofillCategory::Off || category == AutofillCategory::Automatic) && mantle == AutofillMantle::Anchor)
        return defaultLabel();

    // 7. If category is Off, let the element's autofill field name be the string "off", let its
    // autofill hint set be empty, and let its IDL-exposed autofill value be the string "off".
    // Then, abort these steps.
    if (category == AutofillCategory::Off)
        return { off, off.get().string() };

    // 8. If category is Automatic, let the element's autofill field name be the string "on",
    // let its autofill hint set be empty, and let its IDL-exposed autofill value be the string
    // "on". Then, abort these steps.
    if (category == AutofillCategory::Automatic)
        return { on, on.get().string() };

    // 9. Let scope tokens be an empty list.
    // 10. Let hint tokens be an empty set.

    // NOTE: We are ignoring these steps as we don't currently make use of scope tokens or hint tokens anywhere.

    // 11. Let IDL value have the same value as field.
    String idlValue = field;

    // 12, If the indexth token in tokens is the first entry, then skip to the step labeled done.
    if (index == 0)
        return { field, idlValue };

    // 13. Decrement index by one
    index--;

    // 14. If category is Contact and the indexth token in tokens is an ASCII case-insensitive match
    // for one of the strings in the following list, then run the substeps that follow:
    const auto& contactToken = tokens[index];
    if (category == AutofillCategory::Contact && isContactToken(contactToken)) {
        // 1. Let contact be the matching string from the list above.
        const auto& contact = contactToken;

        // 2. Insert contact at the start of scope tokens.
        // 3. Add contact to hint tokens.

        // NOTE: We are ignoring these steps as we don't currently make use of scope tokens or hint tokens anywhere.

        // 4. Let IDL value be the concatenation of contact, a U+0020 SPACE character, and the previous
        // value of IDL value (which at this point will always be field).
        idlValue = WTF::makeString(contact, " ", idlValue);

        // 5. If the indexth entry in tokens is the first entry, then skip to the step labeled done.
        if (index == 0)
            return { field, idlValue };

        // 6. Decrement index by one.
        index--;
    }

    // 15. If the indexth token in tokens is an ASCII case-insensitive match for one of the strings
    // in the following list, then run the substeps that follow:
    const auto& modeToken = tokens[index];
    if (equalIgnoringASCIICase(modeToken, "shipping") || equalIgnoringASCIICase(modeToken, "billing")) {
        // 1. Let mode be the matching string from the list above.
        const auto& mode = modeToken;

        // 2. Insert mode at the start of scope tokens.
        // 3. Add mode to hint tokens.

        // NOTE: We are ignoring these steps as we don't currently make use of scope tokens or hint tokens anywhere.

        // 4. Let IDL value be the concatenation of mode, a U+0020 SPACE character, and the previous
        // value of IDL value (which at this point will either be field or the concatenation of contact,
        // a space, and field).
        idlValue = WTF::makeString(mode, " ", idlValue);

        // 5. If the indexth entry in tokens is the first entry, then skip to the step labeled done.
        if (index == 0)
            return { field, idlValue };

        // 6. Decrement index by one.
        index--;
    }

    // 16. If the indexth entry in tokens is not the first entry, then jump to the step labeled default.
    if (index != 0)
        return defaultLabel();

    // 17. If the first eight characters of the indexth token in tokens are not an ASCII case-insensitive
    // match for the string "section-", then jump to the step labeled default.
    const auto& sectionToken = tokens[index];
    if (!sectionToken.startsWithIgnoringASCIICase("section-"))
        return defaultLabel();

    // 18. Let section be the indexth token in tokens, converted to ASCII lowercase.
    const auto& section = sectionToken;

    // 19. Insert section at the start of scope tokens.

    // NOTE: We are ignoring this step as we don't currently make use of scope tokens or hint tokens anywhere.

    // 20. Let IDL value be the concatenation of section, a U+0020 SPACE character, and the previous
    // value of IDL value.
    idlValue = WTF::makeString(section, " ", idlValue);

    return { field, idlValue };
}
Example #18
0
static bool isLegacySupportedJavaScriptLanguage(const String& language) {
    // Mozilla 1.8 accepts javascript1.0 - javascript1.7, but WinIE 7 accepts only
    // javascript1.1 - javascript1.3.
    // Mozilla 1.8 and WinIE 7 both accept javascript and livescript.
    // WinIE 7 accepts ecmascript and jscript, but Mozilla 1.8 doesn't.
    // Neither Mozilla 1.8 nor WinIE 7 accept leading or trailing whitespace.
    // We want to accept all the values that either of these browsers accept, but
    // not other values.

    // FIXME: This function is not HTML5 compliant. These belong in the MIME
    // registry as "text/javascript<version>" entries.
    return equalIgnoringASCIICase(language, "javascript") ||
           equalIgnoringASCIICase(language, "javascript1.0") ||
           equalIgnoringASCIICase(language, "javascript1.1") ||
           equalIgnoringASCIICase(language, "javascript1.2") ||
           equalIgnoringASCIICase(language, "javascript1.3") ||
           equalIgnoringASCIICase(language, "javascript1.4") ||
           equalIgnoringASCIICase(language, "javascript1.5") ||
           equalIgnoringASCIICase(language, "javascript1.6") ||
           equalIgnoringASCIICase(language, "javascript1.7") ||
           equalIgnoringASCIICase(language, "livescript") ||
           equalIgnoringASCIICase(language, "ecmascript") ||
           equalIgnoringASCIICase(language, "jscript");
}
Example #19
0
bool SizesCalcParser::calcToReversePolishNotation(CSSParserTokenRange range) {
  // This method implements the shunting yard algorithm, to turn the calc syntax
  // into a reverse polish notation.
  // http://en.wikipedia.org/wiki/Shunting-yard_algorithm

  Vector<CSSParserToken> stack;
  while (!range.atEnd()) {
    const CSSParserToken& token = range.consume();
    switch (token.type()) {
      case NumberToken:
        appendNumber(token);
        break;
      case DimensionToken:
        if (!CSSPrimitiveValue::isLength(token.unitType()) ||
            !appendLength(token))
          return false;
        break;
      case DelimiterToken:
        if (!handleOperator(stack, token))
          return false;
        break;
      case FunctionToken:
        if (!equalIgnoringASCIICase(token.value(), "calc"))
          return false;
      // "calc(" is the same as "("
      case LeftParenthesisToken:
        // If the token is a left parenthesis, then push it onto the stack.
        stack.append(token);
        break;
      case RightParenthesisToken:
        // If the token is a right parenthesis:
        // Until the token at the top of the stack is a left parenthesis, pop
        // operators off the stack onto the output queue.
        while (!stack.isEmpty() &&
               stack.last().type() != LeftParenthesisToken &&
               stack.last().type() != FunctionToken) {
          appendOperator(stack.last());
          stack.removeLast();
        }
        // If the stack runs out without finding a left parenthesis, then there
        // are mismatched parentheses.
        if (stack.isEmpty())
          return false;
        // Pop the left parenthesis from the stack, but not onto the output
        // queue.
        stack.removeLast();
        break;
      case WhitespaceToken:
      case EOFToken:
        break;
      case CommentToken:
        ASSERT_NOT_REACHED();
      case CDOToken:
      case CDCToken:
      case AtKeywordToken:
      case HashToken:
      case UrlToken:
      case BadUrlToken:
      case PercentageToken:
      case IncludeMatchToken:
      case DashMatchToken:
      case PrefixMatchToken:
      case SuffixMatchToken:
      case SubstringMatchToken:
      case ColumnToken:
      case UnicodeRangeToken:
      case IdentToken:
      case CommaToken:
      case ColonToken:
      case SemicolonToken:
      case LeftBraceToken:
      case LeftBracketToken:
      case RightBraceToken:
      case RightBracketToken:
      case StringToken:
      case BadStringToken:
        return false;
    }
  }

  // When there are no more tokens to read:
  // While there are still operator tokens in the stack:
  while (!stack.isEmpty()) {
    // If the operator token on the top of the stack is a parenthesis, then
    // there are mismatched parentheses.
    CSSParserTokenType type = stack.last().type();
    if (type == LeftParenthesisToken || type == FunctionToken)
      return false;
    // Pop the operator onto the output queue.
    appendOperator(stack.last());
    stack.removeLast();
  }
  return true;
}