blob: 9712fc267a5795e0e81815765cc6753b24023096 [file] [log] [blame]
// Copyright 2010 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <map>
#include <utility>
#include "pagespeed/kernel/base/basictypes.h"
#include "pagespeed/kernel/base/fast_wildcard_group.h"
#include "pagespeed/kernel/base/scoped_ptr.h"
#include "pagespeed/kernel/base/string.h"
#include "pagespeed/kernel/base/string_util.h"
#include "pagespeed/kernel/http/user_agent_matcher.h"
#include "pagespeed/kernel/util/re2.h"
namespace net_instaweb {
const char UserAgentMatcher::kTestUserAgentWebP[] = "test-user-agent-webp";
// Note that this must not contain the substring "webp".
const char UserAgentMatcher::kTestUserAgentNoWebP[] = "test-user-agent-no";
class RequestHeaders;
// These are the user-agents of browsers/mobile devices which support
// image-inlining. The data is from "Latest WURFL Repository"(mobile devices)
// and "Web Patch"(browsers) on http://wurfl.sourceforge.net
// The user-agent string for Opera could be in the form of "Opera 7" or
// "Opera/7", we use the wildcard pattern "Opera?7" for this case.
namespace {
const char kGooglePlusUserAgent[] =
"*Google (+https://developers.google.com/+/web/snippet/)*";
const char* kImageInliningWhitelist[] = {
"*Android*",
"*Chrome/*",
"*Firefox/*",
"*iPad*",
"*iPhone*",
"*iPod*",
"*itouch*",
"*Opera*",
"*Safari*",
"*Wget*",
// Allow in ads policy checks to match usual UA behavior.
"AdsBot-Google*",
// Plus IE, see use in the code.
// The following user agents are used only for internal testing
"google command line rewriter",
"webp",
"webp-la",
"prefetch_image_tag",
"prefetch_link_script_tag",
};
const char* kImageInliningBlacklist[] = {
"*Firefox/1.*",
"*Firefox/2.*",
"*MSIE 5.*",
"*MSIE 6.*",
"*MSIE 7.*",
"*Opera?5*",
"*Opera?6*",
kGooglePlusUserAgent
};
// Exclude BlackBerry OS 5.0 and older. See
// http://supportforums.blackberry.com/t5/Web-and-WebWorks-Development/How-to-detect-the-BlackBerry-Browser/ta-p/559862
// for details on BlackBerry UAs.
// Exclude all Opera Mini: see bug #1070.
// https://github.com/pagespeed/mod_pagespeed/issues/1070
const char* kLazyloadImagesBlacklist[] = {
"BlackBerry*CLDC*",
"*Opera Mini*",
kGooglePlusUserAgent
};
// For Panels and deferJs the list is same as of now.
// we only allow Firefox4+, IE8+, safari and Chrome
// We'll be updating this as and when required.
// The blacklist is checked first, then if not in there, the whitelist is
// checked.
// Note: None of the following should match a mobile UA.
const char* kPanelSupportDesktopWhitelist[] = {
"*Chrome/*",
"*Firefox/*",
"*Safari*",
// Plus IE, see code below.
"*Wget*",
// The following user agents are used only for internal testing
"prefetch_link_script_tag",
};
// Note that these are combined with kPanelSupportDesktopWhitelist, which
// imply defer_javascript support.
const char* kDeferJSWhitelist[] = {
"*Googlebot*",
"*Mediapartners-Google*"
};
const char* kPanelSupportDesktopBlacklist[] = {
"*Firefox/1.*",
"*Firefox/2.*",
"*Firefox/3.*",
"*MSIE 5.*",
"*MSIE 6.*",
"*MSIE 7.*",
"*MSIE 8.*",
};
const char* kPanelSupportMobileWhitelist[] = {
"*AppleWebKit/*",
};
// Webp support for most devices should be triggered on Accept:image/webp.
// However we special-case Android 4.0 browsers which are fairly commonly
// used, support webp, and don't send Accept:image/webp. Very old versions
// of Chrome may support webp without Accept:image/webp, but it is safe to
// ignore them because they are extremely rare.
//
// For legacy webp rewriting, we whitelist Android, but blacklist
// older versions and Firefox, which includes 'Android' in its UA.
// We do this in 2 stages in order to exclude the following category 1 but
// include category 2.
// 1. Firefox on Android does not support WebP, and it has "Android" and
// "Firefox" in the user agent.
// 2. Recent Opera support WebP, and some Opera have both "Opera" and
// "Firefox" in the user agent.
const char* kLegacyWebpWhitelist[] = {
"*Android *",
};
// Based on https://code.google.com/p/modpagespeed/issues/detail?id=978,
// Desktop IE11 will start masquerading as Chrome soon, and according to
// https://groups.google.com/forum/?utm_medium=email&utm_source=footer#!msg/mod-pagespeed-discuss/HYzzdOzJu_k/ftdV8koVgUEJ
// a browser called Midori might (at some point) masquerade as Chrome as well.
const char* kLegacyWebpBlacklist[] = {
"*Android 0.*",
"*Android 1.*",
"*Android 2.*",
"*Android 3.*",
"*Firefox/*",
"*Edge/*",
"*Trident/*",
"*Windows Phone*",
"*Chrome/*", // Genuine Chrome always sends Accept: webp.
"*CriOS/*", // Paranoia: we should not see Android and CriOS together.
};
// To determine lossless webp support and animated webp support, we must
// examine the UA.
const char* kWebpLosslessAlphaWhitelist[] = {
"*Chrome/??.*",
"*Chrome/???.*",
"*CriOS/??.*",
// User agent used only for internal testing.
"webp-la",
"webp-animated",
};
const char* kWebpLosslessAlphaBlacklist[] = {
"*Chrome/?.*",
"*Chrome/1?.*",
"*Chrome/20.*",
"*Chrome/21.*",
"*Chrome/22.*",
"*CriOS/1?.*",
"*CriOS/20.*",
"*CriOS/21.*",
"*CriOS/22.*",
"*CriOS/23.*",
"*CriOS/24.*",
"*CriOS/25.*",
"*CriOS/26.*",
"*CriOS/27.*",
"*CriOS/28.*",
};
// Animated WebP is supported by browsers based on Chromium v32+, including
// Chrome 32+ and Opera 19+. Because since version 15, Opera has been including
// "Chrome/VERSION" in the user agent string [1], the test for Chrome 32+ will
// also cover Opera 19+.
// [1] https://dev.opera.com/blog/opera-user-agent-strings-opera-15-and-beyond/
const char* kWebpAnimatedWhitelist[] = {
"*Chrome/??.*",
"*CriOS/??.*",
"webp-animated", // User agent for internal testing.
};
const char* kWebpAnimatedBlacklist[] = {
"*Chrome/?.*",
"*Chrome/1?.*",
"*Chrome/2?.*",
"*Chrome/30.*",
"*Chrome/31.*",
"*CriOS/?.*",
"*CriOS/1?.*",
"*CriOS/2?.*",
"*CriOS/30.*",
"*CriOS/31.*",
};
const char* kInsertDnsPrefetchWhitelist[] = {
"*Chrome/*",
"*Firefox/*",
"*Safari/*",
// Plus IE, see code below.
"*Wget*",
// The following user agents are used only for internal testing
"prefetch_image_tag",
};
const char* kInsertDnsPrefetchBlacklist[] = {
"*Firefox/1.*",
"*Firefox/2.*",
"*Firefox/3.*",
// Safari indicates version with a separate Version/N.N.N token that appears
// somewhere before the Safari/ token. This only started with version 3, but
// versions before 3 are 10+ years old at this point and won't run on any
// supported OS.
"*Version/3.*Safari/*",
"*Version/4.*Safari/*",
// 5.0.1+ actually did support it, but that's long obsolete, so don't bother
// contorting the list to include it.
"*Version/5.*Safari/*",
"*MSIE 5.*",
"*MSIE 6.*",
"*MSIE 7.*",
"*MSIE 8.*",
};
// Whitelist used for doing the tablet-user-agent check, which also feeds
// into the device type used for storing properties in the property cache.
const char* kTabletUserAgentWhitelist[] = {
"*Android*", // Android tablet has "Android" but not "Mobile". Regexp
// checks for UserAgents should first check the mobile
// whitelists and blacklists and only then check the tablet
// whitelist for correct results.
"*iPad*",
"*TouchPad*",
"*Silk-Accelerated*",
"*Kindle Fire*"
};
// Whitelist used for doing the mobile-user-agent check, which also feeds
// into the device type used for storing properties in the property cache.
const char* kMobileUserAgentWhitelist[] = {
"*Mozilla*Android*Mobile*",
"*iPhone*",
"*BlackBerry*",
"*Opera Mobi*",
"*Opera Mini*",
"*SymbianOS*",
"*UP.Browser*",
"*J-PHONE*",
"*Profile/MIDP*",
"*profile/MIDP*",
"*portalmmm*",
"*DoCoMo*",
"*Obigo*",
"AdsBot-Google-Mobile",
};
// Blacklist used for doing the mobile-user-agent check.
const char* kMobileUserAgentBlacklist[] = {
"*Mozilla*Android*Silk*Mobile*",
"*Mozilla*Android*Kindle Fire*Mobile*"
};
// Whitelist used for mobilization.
const char* kMobilizationUserAgentWhitelist[] = {
"*Android*",
"*Chrome/*",
"*Firefox/*",
"*iPad*",
"*iPhone*",
"*iPod*",
"*Opera*",
"*Safari*",
"*Wget*",
"*CriOS/*", // Chrome for iOS.
"*Android *", // Native Android browser (see blacklist below).
"*iPhone*",
"AdsBot-Google*"
};
// Blacklist used for doing the mobilization UA check.
const char* kMobilizationUserAgentBlacklist[] = {
"*Android 0.*",
"*Android 1.*",
"*Android 2.*",
"*BlackBerry*",
"*Mozilla*Android*Silk*Mobile*",
"*Mozilla*Android*Kindle Fire*Mobile*"
"*Opera Mobi*",
"*Opera Mini*",
"*SymbianOS*",
"*UP.Browser*",
"*J-PHONE*",
"*Profile/MIDP*",
"*profile/MIDP*",
"*portalmmm*",
"*DoCoMo*",
"*Obigo*",
// TODO(jmaessen): Remove when there's a fix for scroll misbehavior on CriOS.
"*CriOS/*", // Chrome for iOS.
"*GSA*Safari*", // Google Search Application for iOS.
// TODO(jmaessen): Remove when there's a fix for page geometry on the native
// Android browser (the old WebKit browser).
"*U; Android 3.*",
"*U; Android 4.*"
};
// TODO(mmohabey): Tune this to include more browsers.
const char* kSupportsPrefetchImageTag[] = {
"*Chrome/*",
"*Safari/*",
// User agent used only for internal testing
"prefetch_image_tag",
};
const char* kSupportsPrefetchLinkScriptTag[] = {
"*Firefox/*",
// Plus IE, see code below
// User agent used only for internal testing
"prefetch_link_script_tag",
};
// IE 11 and later user agent strings are deliberately difficult. That would be
// great if random pages never put the browser into backward compatibility mode,
// and all the outstanding caching bugs were fixed, but neither is true and so
// we need to be able to spot IE 11 and treat it as IE even though we're not
// supposed to need to do so ever again. See
// http://blogs.msdn.com/b/ieinternals/archive/2013/09/21/internet-explorer-11-user-agent-string-ua-string-sniffing-compatibility-with-gecko-webkit.aspx
const char* kIeUserAgents[] = {
"*MSIE *", // Should match any IE before 11.
"*rv:11.?) like Gecko*", // Other revisions (eg 12.0) are FireFox
"*IE 1*", // Initial numeral avoids Samsung UA
"*Trident/7*", // Opera sometimes pretends to be earlier Trident
};
const int kIEBefore11Index = 0;
// Match either 'CriOS' (iOS Chrome) or 'Chrome'. ':?' marks a non-capturing
// group.
const char* kChromeVersionPattern =
"(?:Chrome|CriOS)/(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+)";
// Device strings must not include wildcards.
struct Dimension {
const char* device_name;
int width;
int height;
};
const Dimension kKnownScreenDimensions[] = {
{"Galaxy Nexus", 720, 1280},
{"GT-I9300", 720, 1280},
{"GT-N7100", 720, 1280},
{"Nexus 4", 768, 1280},
{"Nexus 10", 1600, 2560},
{"Nexus S", 480, 800},
{"Xoom", 800, 1280},
{"XT907", 540, 960},
};
} // namespace
// Note that "blink" here does not mean the new Chrome rendering
// engine. It refers to a pre-existing internal name for the
// technology behind partial HTML caching:
// https://developers.google.com/speed/pagespeed/service/CacheHtml
UserAgentMatcher::UserAgentMatcher()
: chrome_version_pattern_(kChromeVersionPattern) {
// Initialize FastWildcardGroup for image inlining whitelist & blacklist.
for (int i = 0, n = arraysize(kImageInliningWhitelist); i < n; ++i) {
supports_image_inlining_.Allow(kImageInliningWhitelist[i]);
}
for (int i = 0, n = arraysize(kIeUserAgents); i < n; ++i) {
supports_image_inlining_.Allow(kIeUserAgents[i]);
}
for (int i = 0, n = arraysize(kImageInliningBlacklist); i < n; ++i) {
supports_image_inlining_.Disallow(kImageInliningBlacklist[i]);
}
for (int i = 0, n = arraysize(kLazyloadImagesBlacklist); i < n; ++i) {
supports_lazyload_images_.Disallow(kLazyloadImagesBlacklist[i]);
}
for (int i = 0, n = arraysize(kPanelSupportDesktopWhitelist); i < n; ++i) {
blink_desktop_whitelist_.Allow(kPanelSupportDesktopWhitelist[i]);
// Explicitly allowed blink UAs should also allow defer_javascript.
defer_js_whitelist_.Allow(kPanelSupportDesktopWhitelist[i]);
}
blink_desktop_whitelist_.Allow(kIeUserAgents[kIEBefore11Index]);
defer_js_whitelist_.Allow(kIeUserAgents[kIEBefore11Index]);
for (int i = 0, n = arraysize(kDeferJSWhitelist); i < n; ++i) {
defer_js_whitelist_.Allow(kDeferJSWhitelist[i]);
}
// https://code.google.com/p/modpagespeed/issues/detail?id=982
defer_js_whitelist_.Disallow("* MSIE 9.*");
for (int i = 0, n = arraysize(kPanelSupportDesktopBlacklist); i < n; ++i) {
blink_desktop_blacklist_.Allow(kPanelSupportDesktopBlacklist[i]);
// Explicitly disallowed blink UAs should also disable defer_javascript.
defer_js_whitelist_.Disallow(kPanelSupportDesktopBlacklist[i]);
}
for (int i = 0, n = arraysize(kPanelSupportMobileWhitelist); i < n; ++i) {
blink_mobile_whitelist_.Allow(kPanelSupportMobileWhitelist[i]);
}
// Do the same for webp support.
for (int i = 0, n = arraysize(kLegacyWebpWhitelist); i < n; ++i) {
legacy_webp_.Allow(kLegacyWebpWhitelist[i]);
}
for (int i = 0, n = arraysize(kLegacyWebpBlacklist); i < n; ++i) {
legacy_webp_.Disallow(kLegacyWebpBlacklist[i]);
}
for (int i = 0, n = arraysize(kWebpLosslessAlphaWhitelist); i < n; ++i) {
supports_webp_lossless_alpha_.Allow(kWebpLosslessAlphaWhitelist[i]);
}
for (int i = 0, n = arraysize(kWebpLosslessAlphaBlacklist); i < n; ++i) {
supports_webp_lossless_alpha_.Disallow(kWebpLosslessAlphaBlacklist[i]);
}
for (int i = 0, n = arraysize(kWebpAnimatedWhitelist); i < n; ++i) {
supports_webp_animated_.Allow(kWebpAnimatedWhitelist[i]);
}
for (int i = 0, n = arraysize(kWebpAnimatedBlacklist); i < n; ++i) {
supports_webp_animated_.Disallow(kWebpAnimatedBlacklist[i]);
}
for (int i = 0, n = arraysize(kSupportsPrefetchImageTag); i < n; ++i) {
supports_prefetch_image_tag_.Allow(kSupportsPrefetchImageTag[i]);
}
for (int i = 0, n = arraysize(kSupportsPrefetchLinkScriptTag); i < n; ++i) {
supports_prefetch_link_script_tag_.Allow(kSupportsPrefetchLinkScriptTag[i]);
}
for (int i = 0, n = arraysize(kIeUserAgents); i < n; ++i) {
supports_prefetch_link_script_tag_.Allow(kIeUserAgents[i]);
}
for (int i = 0, n = arraysize(kInsertDnsPrefetchWhitelist); i < n; ++i) {
supports_dns_prefetch_.Allow(kInsertDnsPrefetchWhitelist[i]);
}
for (int i = 0, n = arraysize(kIeUserAgents); i < n; ++i) {
supports_dns_prefetch_.Allow(kIeUserAgents[i]);
}
for (int i = 0, n = arraysize(kInsertDnsPrefetchBlacklist); i < n; ++i) {
supports_dns_prefetch_.Disallow(kInsertDnsPrefetchBlacklist[i]);
}
for (int i = 0, n = arraysize(kMobileUserAgentWhitelist); i < n; ++i) {
mobile_user_agents_.Allow(kMobileUserAgentWhitelist[i]);
}
for (int i = 0, n = arraysize(kMobileUserAgentBlacklist); i < n; ++i) {
mobile_user_agents_.Disallow(kMobileUserAgentBlacklist[i]);
}
for (int i = 0, n = arraysize(kTabletUserAgentWhitelist); i < n; ++i) {
tablet_user_agents_.Allow(kTabletUserAgentWhitelist[i]);
}
for (int i = 0, n = arraysize(kMobilizationUserAgentWhitelist); i < n;
++i) {
mobilization_user_agents_.Allow(kMobilizationUserAgentWhitelist[i]);
}
for (int i = 0, n = arraysize(kMobilizationUserAgentBlacklist); i < n;
++i) {
mobilization_user_agents_.Disallow(kMobilizationUserAgentBlacklist[i]);
}
for (int i = 0, n = arraysize(kIeUserAgents); i < n; ++i) {
ie_user_agents_.Allow(kIeUserAgents[i]);
}
GoogleString known_devices_pattern_string = "(";
for (int i = 0, n = arraysize(kKnownScreenDimensions); i < n; ++i) {
const Dimension& dim = kKnownScreenDimensions[i];
screen_dimensions_map_[dim.device_name] = make_pair(dim.width, dim.height);
if (i != 0) {
StrAppend(&known_devices_pattern_string, "|");
}
StrAppend(&known_devices_pattern_string, dim.device_name);
}
StrAppend(&known_devices_pattern_string, ")");
known_devices_pattern_.reset(new RE2(known_devices_pattern_string));
}
UserAgentMatcher::~UserAgentMatcher() {
}
bool UserAgentMatcher::IsIe(const StringPiece& user_agent) const {
return ie_user_agents_.Match(user_agent, false);
}
bool UserAgentMatcher::IsIe9(const StringPiece& user_agent) const {
return user_agent.find(" MSIE 9.") != GoogleString::npos;
}
bool UserAgentMatcher::SupportsImageInlining(
const StringPiece& user_agent) const {
if (user_agent.empty()) {
return true;
}
return supports_image_inlining_.Match(user_agent, false);
}
bool UserAgentMatcher::SupportsLazyloadImages(StringPiece user_agent) const {
return supports_lazyload_images_.Match(user_agent, true);
}
UserAgentMatcher::BlinkRequestType UserAgentMatcher::GetBlinkRequestType(
const char* user_agent, const RequestHeaders* request_headers) const {
if (user_agent == NULL || user_agent[0] == '\0') {
return kNullOrEmpty;
}
if (GetDeviceTypeForUAAndHeaders(user_agent, request_headers) != kDesktop) {
if (blink_mobile_whitelist_.Match(user_agent, false)) {
return kBlinkWhiteListForMobile;
}
return kDoesNotSupportBlinkForMobile;
}
if (blink_desktop_blacklist_.Match(user_agent, false)) {
return kBlinkBlackListForDesktop;
}
if (blink_desktop_whitelist_.Match(user_agent, false)) {
return kBlinkWhiteListForDesktop;
}
return kDoesNotSupportBlink;
}
UserAgentMatcher::PrefetchMechanism UserAgentMatcher::GetPrefetchMechanism(
const StringPiece& user_agent) const {
// Chrome >= 42 has link rel=prefetch that's good at actually using the
// prefetch result, prioritize using that.
int major, minor, build, patch;
if (GetChromeBuildNumber(user_agent, &major, &minor, &build, &patch)
&& major >= 42) {
return kPrefetchLinkRelPrefetchTag;
}
if (supports_prefetch_image_tag_.Match(user_agent, false)) {
return kPrefetchImageTag;
} else if (supports_prefetch_link_script_tag_.Match(user_agent, false)) {
return kPrefetchLinkScriptTag;
}
return kPrefetchNotSupported;
}
bool UserAgentMatcher::SupportsDnsPrefetch(
const StringPiece& user_agent) const {
return supports_dns_prefetch_.Match(user_agent, false);
}
bool UserAgentMatcher::SupportsJsDefer(const StringPiece& user_agent,
bool allow_mobile) const {
// TODO(ksimbili): Use IsMobileRequest?
if (GetDeviceTypeForUA(user_agent) != kDesktop) {
return allow_mobile && blink_mobile_whitelist_.Match(user_agent, false);
}
return user_agent.empty() || defer_js_whitelist_.Match(user_agent, false);
}
bool UserAgentMatcher::LegacyWebp(const StringPiece& user_agent) const {
return legacy_webp_.Match(user_agent, false);
}
bool UserAgentMatcher::SupportsWebpLosslessAlpha(
const StringPiece& user_agent) const {
return supports_webp_lossless_alpha_.Match(user_agent, false);
}
bool UserAgentMatcher::SupportsWebpAnimated(
const StringPiece& user_agent) const {
return supports_webp_animated_.Match(user_agent, false);
}
UserAgentMatcher::DeviceType UserAgentMatcher::GetDeviceTypeForUAAndHeaders(
const StringPiece& user_agent,
const RequestHeaders* request_headers) const {
return GetDeviceTypeForUA(user_agent);
}
bool UserAgentMatcher::IsAndroidUserAgent(const StringPiece& user_agent) const {
return user_agent.find("Android") != GoogleString::npos;
}
bool UserAgentMatcher::IsiOSUserAgent(const StringPiece& user_agent) const {
return user_agent.find("iPhone") != GoogleString::npos ||
user_agent.find("iPad") != GoogleString::npos;
}
bool UserAgentMatcher::GetChromeBuildNumber(const StringPiece& user_agent,
int* major, int* minor, int* build,
int* patch) const {
return RE2::PartialMatch(StringPieceToRe2(user_agent),
chrome_version_pattern_, major, minor, build, patch);
}
bool UserAgentMatcher::SupportsDnsPrefetchUsingRelPrefetch(
const StringPiece& user_agent) const {
return IsIe9(user_agent);
}
bool UserAgentMatcher::SupportsSplitHtml(const StringPiece& user_agent,
bool allow_mobile) const {
return SupportsJsDefer(user_agent, allow_mobile);
}
// TODO(bharathbhushan): Make sure GetDeviceTypeForUA is called only once per
// http request.
UserAgentMatcher::DeviceType UserAgentMatcher::GetDeviceTypeForUA(
const StringPiece& user_agent) const {
if (mobile_user_agents_.Match(user_agent, false)) {
return kMobile;
}
if (tablet_user_agents_.Match(user_agent, false)) {
return kTablet;
}
return kDesktop;
}
StringPiece UserAgentMatcher::DeviceTypeString(DeviceType device_type) {
StringPiece device_type_suffix = "";
switch (device_type) {
case kMobile:
device_type_suffix = "mobile";
break;
case kTablet:
device_type_suffix = "tablet";
break;
case kDesktop:
case kEndOfDeviceType:
default:
device_type_suffix = "desktop";
break;
}
return device_type_suffix;
}
StringPiece UserAgentMatcher::DeviceTypeSuffix(DeviceType device_type) {
StringPiece device_type_suffix = "";
switch (device_type) {
case kMobile:
device_type_suffix = "@Mobile";
break;
case kTablet:
device_type_suffix = "@Tablet";
break;
case kDesktop:
case kEndOfDeviceType:
default:
device_type_suffix = "@Desktop";
break;
}
return device_type_suffix;
}
bool UserAgentMatcher::UserAgentExceedsChromeiOSBuildAndPatch(
const StringPiece& user_agent, int required_build,
int required_patch) const {
// Verify if this is an iOS user agent.
if (!IsiOSUserAgent(user_agent)) {
return false;
}
return UserAgentExceedsChromeBuildAndPatch(
user_agent, required_build, required_patch);
}
bool UserAgentMatcher::UserAgentExceedsChromeAndroidBuildAndPatch(
const StringPiece& user_agent, int required_build,
int required_patch) const {
// Verify if this is an Android user agent.
if (!IsAndroidUserAgent(user_agent)) {
return false;
}
return UserAgentExceedsChromeBuildAndPatch(
user_agent, required_build, required_patch);
}
bool UserAgentMatcher::UserAgentExceedsChromeBuildAndPatch(
const StringPiece& user_agent, int required_build,
int required_patch) const {
// By default user agent sniffing is disabled.
if (required_build == -1 && required_patch == -1) {
return false;
}
int major = -1;
int minor = -1;
int parsed_build = -1;
int parsed_patch = -1;
if (!GetChromeBuildNumber(user_agent, &major, &minor,
&parsed_build, &parsed_patch)) {
return false;
}
if (parsed_build < required_build) {
return false;
} else if (parsed_build == required_build && parsed_patch < required_patch) {
return false;
}
return true;
}
bool UserAgentMatcher::SupportsMobilization(
StringPiece user_agent) const {
return mobilization_user_agents_.Match(user_agent, false);
}
} // namespace net_instaweb