| /* |
| * Copyright 2010 Google Inc. |
| * |
| * 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 "net/instaweb/rewriter/public/rewrite_options.h" |
| |
| #include <algorithm> |
| #include <cstddef> |
| #include <set> |
| #include <utility> |
| |
| #include "base/logging.h" |
| #include "net/instaweb/rewriter/public/domain_lawyer.h" |
| #include "net/instaweb/rewriter/public/experiment_util.h" |
| #include "net/instaweb/rewriter/public/file_load_policy.h" |
| #include "pagespeed/kernel/base/abstract_mutex.h" |
| #include "pagespeed/kernel/base/base64_util.h" |
| #include "pagespeed/kernel/base/basictypes.h" |
| #include "pagespeed/kernel/base/dynamic_annotations.h" // RunningOnValgrind |
| #include "pagespeed/kernel/base/hasher.h" |
| #include "pagespeed/kernel/base/message_handler.h" |
| #include "pagespeed/kernel/base/null_message_handler.h" |
| #include "pagespeed/kernel/base/null_rw_lock.h" |
| #include "pagespeed/kernel/base/rde_hash_map.h" |
| #include "pagespeed/kernel/base/stl_util.h" |
| #include "pagespeed/kernel/base/time_util.h" |
| #include "pagespeed/kernel/base/timer.h" |
| #include "pagespeed/kernel/cache/purge_set.h" |
| #include "pagespeed/kernel/http/google_url.h" |
| #include "pagespeed/kernel/http/http_options.h" |
| #include "pagespeed/kernel/http/request_headers.h" |
| #include "pagespeed/kernel/http/semantic_type.h" |
| #include "pagespeed/kernel/http/user_agent_matcher.h" |
| |
| namespace net_instaweb { |
| |
| // Option names. |
| // TODO(matterbury): Evaluate these filters to check which ones aren't global, |
| // rather are (say) Apache specific, and move them out. |
| // TODO(jmarantz): Use consistent naming from semantic_type.h for all option |
| // names that reference css/styles/js/scripts etc. such as CssPreserveUrls. |
| const char RewriteOptions::kAddOptionsToUrls[] = "AddOptionsToUrls"; |
| const char RewriteOptions::kAcceptInvalidSignatures[] = |
| "AcceptInvalidSignatures"; |
| const char RewriteOptions::kAccessControlAllowOrigins[] = |
| "AccessControlAllowOrigins"; |
| const char RewriteOptions::kAllowLoggingUrlsInLogRecord[] = |
| "AllowLoggingUrlsInLogRecord"; |
| const char RewriteOptions::kAllowOptionsToBeSetByCookies[] = |
| "AllowOptionsToBeSetByCookies"; |
| const char RewriteOptions::kAlwaysMobilize[] = "AlwaysMobilize"; |
| const char RewriteOptions::kAlwaysRewriteCss[] = "AlwaysRewriteCss"; |
| const char RewriteOptions::kAnalyticsID[] = "AnalyticsID"; |
| const char RewriteOptions::kAvoidRenamingIntrospectiveJavascript[] = |
| "AvoidRenamingIntrospectiveJavascript"; |
| const char RewriteOptions::kAwaitPcacheLookup[] = "AwaitPcacheLookup"; |
| const char RewriteOptions::kBeaconReinstrumentTimeSec[] = |
| "BeaconReinstrumentTimeSec"; |
| const char RewriteOptions::kBeaconUrl[] = "BeaconUrl"; |
| const char RewriteOptions::kBlinkMaxHtmlSizeRewritable[] = |
| "BlinkMaxHtmlSizeRewritable"; |
| const char RewriteOptions::kCacheFragment[] = "CacheFragment"; |
| const char RewriteOptions::kCacheSmallImagesUnrewritten[] = |
| "CacheSmallImagesUnrewritten"; |
| const char RewriteOptions::kClientDomainRewrite[] = "ClientDomainRewrite"; |
| const char RewriteOptions::kCombineAcrossPaths[] = "CombineAcrossPaths"; |
| const char RewriteOptions::kCompressMetadataCache[] = "CompressMetadataCache"; |
| const char RewriteOptions::kContentExperimentID[] = "ContentExperimentID"; |
| const char RewriteOptions::kContentExperimentVariantID[] = |
| "ContentExperimentVariantID"; |
| const char RewriteOptions::kCriticalImagesBeaconEnabled[] = |
| "CriticalImagesBeaconEnabled"; |
| const char RewriteOptions::kCriticalLineConfig[] = "CriticalLineConfig"; |
| const char RewriteOptions::kCssFlattenMaxBytes[] = "CssFlattenMaxBytes"; |
| const char RewriteOptions::kCssImageInlineMaxBytes[] = "CssImageInlineMaxBytes"; |
| const char RewriteOptions::kCssInlineMaxBytes[] = "CssInlineMaxBytes"; |
| const char RewriteOptions::kCssOutlineMinBytes[] = "CssOutlineMinBytes"; |
| const char RewriteOptions::kCssPreserveURLs[] = "CssPreserveURLs"; |
| const char RewriteOptions::kDefaultCacheHtml[] = "DefaultCacheHtml"; |
| const char RewriteOptions::kDisableRewriteOnNoTransform[] = |
| "DisableRewriteOnNoTransform"; |
| const char RewriteOptions::kDisableBackgroundFetchesForBots[] = |
| "DisableBackgroundFetchesForBots"; |
| const char RewriteOptions::kDistributeFetches[] = "DistributeFetches"; |
| const char RewriteOptions::kDistributedRewriteKey[] = "DistributedRewriteKey"; |
| const char RewriteOptions::kDistributedRewriteServers[] = |
| "DistributedRewriteServers"; |
| const char RewriteOptions::kDistributedRewriteTimeoutMs[] = |
| "DistributedRewriteTimeoutMs"; |
| const char RewriteOptions::kDomainRewriteCookies[] = |
| "DomainRewriteCookies"; |
| const char RewriteOptions::kDomainRewriteHyperlinks[] = |
| "DomainRewriteHyperlinks"; |
| const char RewriteOptions::kDomainShardCount[] = "DomainShardCount"; |
| const char RewriteOptions::kDownstreamCachePurgeMethod[] = |
| "DownstreamCachePurgeMethod"; |
| const char RewriteOptions::kDownstreamCacheRebeaconingKey[] = |
| "DownstreamCacheRebeaconingKey"; |
| const char RewriteOptions::kDownstreamCacheRewrittenPercentageThreshold[] = |
| "DownstreamCacheRewrittenPercentageThreshold"; |
| const char RewriteOptions::kEnableAggressiveRewritersForMobile[] = |
| "EnableAggressiveRewritersForMobile"; |
| const char RewriteOptions::kEnableBlinkHtmlChangeDetection[] = |
| "EnableBlinkHtmlChangeDetection"; |
| const char RewriteOptions::kEnableBlinkHtmlChangeDetectionLogging[] = |
| "EnableBlinkHtmlChangeDetectionLogging"; |
| const char RewriteOptions::kEnableDeferJsExperimental[] = |
| "EnableDeferJsExperimental"; |
| const char RewriteOptions::kEnableCachePurge[] = "EnableCachePurge"; |
| const char RewriteOptions::kEnableFlushEarlyCriticalCss[] = |
| "EnableFlushEarlyCriticalCss"; |
| const char RewriteOptions::kEnableExtendedInstrumentation[] = |
| "EnableExtendedInstrumentation"; |
| const char RewriteOptions::kEnableLazyLoadHighResImages[] = |
| "EnableLazyLoadHighResImages"; |
| const char RewriteOptions::kEnablePrioritizingScripts[] = |
| "EnablePrioritizingScripts"; |
| const char RewriteOptions::kEnabled[] = "EnableRewriting"; |
| const char RewriteOptions::kEnrollExperiment[] = "EnrollExperiment"; |
| const char RewriteOptions::kExperimentCookieDurationMs[] = |
| "ExperimentCookieDurationMs"; |
| const char RewriteOptions::kExperimentSlot[] = "ExperimentSlot"; |
| const char RewriteOptions::kFetcherProxy[] = "FetchProxy"; |
| const char RewriteOptions::kFinderPropertiesCacheExpirationTimeMs[] = |
| "FinderPropertiesCacheExpirationTimeMs"; |
| const char RewriteOptions::kFinderPropertiesCacheRefreshTimeMs[] = |
| "FinderPropertiesCacheRefreshTimeMs"; |
| const char RewriteOptions::kFlushBufferLimitBytes[] = "FlushBufferLimitBytes"; |
| const char RewriteOptions::kFlushHtml[] = "FlushHtml"; |
| const char RewriteOptions::kFlushMoreResourcesEarlyIfTimePermits[] = |
| "FlushMoreResourcesEarlyIfTimePermits"; |
| const char RewriteOptions::kForbidAllDisabledFilters[] = |
| "ForbidAllDisabledFilters"; |
| const char RewriteOptions::kGoogleFontCssInlineMaxBytes[] = |
| "GoogleFontCssInlineMaxBytes"; |
| const char RewriteOptions::kHideRefererUsingMeta[] = "HideRefererUsingMeta"; |
| const char RewriteOptions::kHttpCacheCompressionLevel[] = |
| "HttpCacheCompressionLevel"; |
| const char RewriteOptions::kIdleFlushTimeMs[] = "IdleFlushTimeMs"; |
| const char RewriteOptions::kImageInlineMaxBytes[] = "ImageInlineMaxBytes"; |
| const char RewriteOptions::kImageJpegNumProgressiveScans[] = |
| "ImageJpegNumProgressiveScans"; |
| const char RewriteOptions::kImageJpegNumProgressiveScansForSmallScreens[] = |
| "ImageJpegNumProgressiveScansForSmallScreens"; |
| const char RewriteOptions::kImageJpegRecompressionQuality[] = |
| "JpegRecompressionQuality"; |
| const char RewriteOptions::kImageJpegRecompressionQualityForSmallScreens[] = |
| "JpegRecompressionQualityForSmallScreens"; |
| const char RewriteOptions::kImageJpegQualityForSaveData[] = |
| "JpegQualityForSaveData"; |
| const char RewriteOptions::kImageLimitOptimizedPercent[] = |
| "ImageLimitOptimizedPercent"; |
| const char RewriteOptions::kImageLimitRenderedAreaPercent[] = |
| "ImageLimitRenderedAreaPercent"; |
| const char RewriteOptions::kImageLimitResizeAreaPercent[] = |
| "ImageLimitResizeAreaPercent"; |
| const char RewriteOptions::kImageMaxRewritesAtOnce[] = "ImageMaxRewritesAtOnce"; |
| const char RewriteOptions::kImagePreserveURLs[] = "ImagePreserveURLs"; |
| const char RewriteOptions::kImageRecompressionQuality[] = |
| "ImageRecompressionQuality"; |
| const char RewriteOptions::kImageResolutionLimitBytes[] = |
| "ImageResolutionLimitBytes"; |
| const char RewriteOptions::kImageWebpRecompressionQuality[] = |
| "WebpRecompressionQuality"; |
| const char RewriteOptions::kImageWebpRecompressionQualityForSmallScreens[] = |
| "WebpRecompressionQualityForSmallScreens"; |
| const char RewriteOptions::kImageWebpAnimatedRecompressionQuality[] = |
| "WebpAnimatedRecompressionQuality"; |
| const char RewriteOptions::kImageWebpQualityForSaveData[] = |
| "WebpQualityForSaveData"; |
| const char RewriteOptions::kImageWebpTimeoutMs[] = "WebpTimeoutMs"; |
| const char RewriteOptions::kImplicitCacheTtlMs[] = "ImplicitCacheTtlMs"; |
| const char RewriteOptions::kInPlaceResourceOptimization[] = |
| "InPlaceResourceOptimization"; |
| const char RewriteOptions::kInPlaceWaitForOptimized[] = |
| "InPlaceWaitForOptimized"; |
| const char RewriteOptions::kInPlacePreemptiveRewriteCss[] = |
| "InPlacePreemptiveRewriteCss"; |
| const char RewriteOptions::kInPlacePreemptiveRewriteCssImages[] = |
| "InPlacePreemptiveRewriteCssImages"; |
| const char RewriteOptions::kInPlacePreemptiveRewriteImages[] = |
| "InPlacePreemptiveRewriteImages"; |
| const char RewriteOptions::kInPlacePreemptiveRewriteJavascript[] = |
| "InPlacePreemptiveRewriteJavascript"; |
| const char RewriteOptions::kInPlaceRewriteDeadlineMs[] = |
| "InPlaceRewriteDeadlineMs"; |
| const char RewriteOptions::kIncreaseSpeedTracking[] = "IncreaseSpeedTracking"; |
| const char RewriteOptions::kInlineOnlyCriticalImages[] = |
| "InlineOnlyCriticalImages"; |
| const char RewriteOptions::kJsInlineMaxBytes[] = "JsInlineMaxBytes"; |
| const char RewriteOptions::kJsOutlineMinBytes[] = "JsOutlineMinBytes"; |
| const char RewriteOptions::kJsPreserveURLs[] = "JsPreserveURLs"; |
| const char RewriteOptions::kLazyloadImagesAfterOnload[] = |
| "LazyloadImagesAfterOnload"; |
| const char RewriteOptions::kLazyloadImagesBlankUrl[] = "LazyloadImagesBlankUrl"; |
| const char RewriteOptions::kLoadFromFileCacheTtlMs[] = "LoadFromFileCacheTtlMs"; |
| const char RewriteOptions::kLogBackgroundRewrite[] = "LogBackgroundRewrite"; |
| const char RewriteOptions::kLogMobilizationSamples[] = "LogMobilizationSamples"; |
| const char RewriteOptions::kLogRewriteTiming[] = "LogRewriteTiming"; |
| const char RewriteOptions::kLogUrlIndices[] = "LogUrlIndices"; |
| const char RewriteOptions::kLowercaseHtmlNames[] = "LowercaseHtmlNames"; |
| const char RewriteOptions::kMaxCacheableResponseContentLength[] = |
| "MaxCacheableContentLength"; |
| const char RewriteOptions::kMaxCombinedCssBytes[] = "MaxCombinedCssBytes"; |
| const char RewriteOptions::kMaxCombinedJsBytes[] = "MaxCombinedJsBytes"; |
| const char RewriteOptions::kMaxHtmlCacheTimeMs[] = "MaxHtmlCacheTimeMs"; |
| const char RewriteOptions::kMaxHtmlParseBytes[] = "MaxHtmlParseBytes"; |
| const char RewriteOptions::kMaxImageBytesForWebpInCss[] = |
| "MaxImageBytesForWebpInCss"; |
| const char RewriteOptions::kMaxImageSizeLowResolutionBytes[] = |
| "MaxImageSizeLowResolutionBytes"; |
| const char RewriteOptions::kMaxInlinedPreviewImagesIndex[] = |
| "MaxInlinedPreviewImagesIndex"; |
| const char RewriteOptions::kMaxLowResImageSizeBytes[] = |
| "MaxLowResImageSizeBytes"; |
| const char RewriteOptions::kMaxLowResToHighResImageSizePercentage[] = |
| "MaxLowResToHighResImageSizePercentage"; |
| const char RewriteOptions::kMaxPrefetchJsElements[] = "MaxPrefetchJsElements"; |
| const char RewriteOptions::kMaxRewriteInfoLogSize[] = "MaxRewriteInfoLogSize"; |
| const char RewriteOptions::kMaxUrlSegmentSize[] = "MaxSegmentLength"; |
| const char RewriteOptions::kMaxUrlSize[] = "MaxUrlSize"; |
| const char RewriteOptions::kMetadataCacheStalenessThresholdMs[] = |
| "MetadataCacheStalenessThresholdMs"; |
| const char RewriteOptions::kMinCacheTtlMs[] = "MinCacheTtlMs"; |
| const char RewriteOptions::kMinImageSizeLowResolutionBytes[] = |
| "MinImageSizeLowResolutionBytes"; |
| const char RewriteOptions::kMinResourceCacheTimeToRewriteMs[] = |
| "MinResourceCacheTimeToRewriteMs"; |
| const char RewriteOptions::kMobBeaconCategory[] = "MobBeaconCategory"; |
| const char RewriteOptions::kMobBeaconUrl[] = "MobBeaconUrl"; |
| const char RewriteOptions::kMobMapLocation[] = "MobMapLocation"; |
| const char RewriteOptions::kMobPhoneNumber[] = "MobPhoneNumber"; |
| const char RewriteOptions::kMobConversionId[] = "MobConversionId"; |
| const char RewriteOptions::kMobMapConversionLabel[] = |
| "MobMapConversionLabel"; |
| const char RewriteOptions::kMobPhoneConversionLabel[] = |
| "MobPhoneConversionLabel"; |
| const char RewriteOptions::kMobConfig[] = "MobConfig"; |
| const char RewriteOptions::kMobIframe[] = "MobIframe"; |
| const char RewriteOptions::kMobIframeDisable[] = "MobIframeDisable"; |
| const char RewriteOptions::kMobIframeViewport[] = "MobIframeViewport"; |
| const char RewriteOptions::kMobLayout[] = "MobLayout"; |
| const char RewriteOptions::kMobNav[] = "MobNav"; |
| const char RewriteOptions::kMobLabeledMode[] = "MobLabeledMode"; |
| const char RewriteOptions::kMobNavClasses[] = "MobNavigationalClasses"; |
| const char RewriteOptions::kMobStatic[] = "MobStatic"; |
| const char RewriteOptions::kMobTheme[] = "MobTheme"; |
| const char RewriteOptions::kModifyCachingHeaders[] = "ModifyCachingHeaders"; |
| const char RewriteOptions::kNoop[] = "Noop"; |
| const char RewriteOptions::kNoTransformOptimizedImages[] = |
| "NoTransformOptimizedImages"; |
| const char RewriteOptions::kNonCacheablesForCachePartialHtml[] = |
| "NonCacheablesForCachePartialHtml"; |
| const char RewriteOptions::kObliviousPagespeedUrls[] = "ObliviousPagespeedUrls"; |
| const char RewriteOptions::kOptionCookiesDurationMs[] = |
| "OptionCookiesDurationMs"; |
| const char RewriteOptions::kOverrideCachingTtlMs[] = "OverrideCachingTtlMs"; |
| const char RewriteOptions::kPreserveSubresourceHints[] = |
| "PreserveSubresourceHints"; |
| const char RewriteOptions::kPreserveUrlRelativity[] = "PreserveUrlRelativity"; |
| const char RewriteOptions::kPrivateNotVaryForIE[] = "PrivateNotVaryForIE"; |
| const char RewriteOptions::kPubliclyCacheMismatchedHashesExperimental[] = |
| "PubliclyCacheMismatchedHashesExperimental"; |
| const char RewriteOptions::kProactivelyFreshenUserFacingRequest[] = |
| "ProactivelyFreshenUserFacingRequest"; |
| const char RewriteOptions::kProactiveResourceFreshening[] = |
| "ProactiveResourceFreshening"; |
| const char RewriteOptions::kProgressiveJpegMinBytes[] = |
| "ProgressiveJpegMinBytes"; |
| const char RewriteOptions::kRejectBlacklisted[] = "RejectBlacklisted"; |
| const char RewriteOptions::kRejectBlacklistedStatusCode[] = |
| "RejectBlacklistedStatusCode"; |
| const char RewriteOptions::kReportUnloadTime[] = "ReportUnloadTime"; |
| const char RewriteOptions::kRespectVary[] = "RespectVary"; |
| const char RewriteOptions::kRespectXForwardedProto[] = "RespectXForwardedProto"; |
| const char RewriteOptions::kResponsiveImageDensities[] = |
| "ResponsiveImageDensities"; |
| const char RewriteOptions::kRewriteDeadlineMs[] = "RewriteDeadlinePerFlushMs"; |
| const char RewriteOptions::kRewriteLevel[] = "RewriteLevel"; |
| const char RewriteOptions::kRewriteRandomDropPercentage[] = |
| "RewriteRandomDropPercentage"; |
| const char RewriteOptions::kRewriteUncacheableResources[] = |
| "RewriteUncacheableResources"; |
| const char RewriteOptions::kRunningExperiment[] = "RunExperiment"; |
| const char RewriteOptions::kServeGhostClickBusterWithSplitHtml[] = |
| "ServeGhostClickBusterWithSplitHtml"; |
| const char RewriteOptions::kServeSplitHtmlInTwoChunks[] = |
| "ServeSplitHtmlInTwoChunks"; |
| const char RewriteOptions::kServeStaleIfFetchError[] = "ServeStaleIfFetchError"; |
| const char RewriteOptions::kServeStaleWhileRevalidateThresholdSec[] = |
| "ServeStaleWhileRevalidateThresholdSec"; |
| const char RewriteOptions::kServeXhrAccessControlHeaders[] = |
| "ServeXhrAccessControlHeaders"; |
| const char RewriteOptions::kStickyQueryParameters[] = "StickyQueryParameters"; |
| const char RewriteOptions::kSupportNoScriptEnabled[] = "SupportNoScriptEnabled"; |
| const char |
| RewriteOptions::kTestOnlyPrioritizeCriticalCssDontApplyOriginalCss[] = |
| "TestOnlyPrioritizeCriticalCssDontApplyOriginalCss"; |
| const char RewriteOptions::kUseBlankImageForInlinePreview[] = |
| "UseBlankImageForInlinePreview"; |
| const char RewriteOptions::kUseExperimentalJsMinifier[] = |
| "UseExperimentalJsMinifier"; |
| const char RewriteOptions::kUseFallbackPropertyCacheValues[] = |
| "UseFallbackPropertyCacheValues"; |
| const char RewriteOptions::kUseImageScanlineApi[] = "UseImageScanlineApi"; |
| const char RewriteOptions::kUseSmartDiffInBlink[] = "UseSmartDiffInBlink"; |
| const char RewriteOptions::kXModPagespeedHeaderValue[] = |
| "XHeaderValue"; |
| const char RewriteOptions::kXPsaBlockingRewrite[] = "BlockingRewriteKey"; |
| |
| const char RewriteOptions::kAllow[] = "Allow"; |
| const char RewriteOptions::kAllowVaryOn[] = "AllowVaryOn"; |
| const char RewriteOptions::kBlockingRewriteRefererUrls[] = |
| "BlockingRewriteRefererUrls"; |
| const char RewriteOptions::kDisableFilters[] = "DisableFilters"; |
| const char RewriteOptions::kDisallow[] = "Disallow"; |
| const char RewriteOptions::kDistributableFilters[] = "DistributableFilters"; |
| const char RewriteOptions::kDomain[] = "Domain"; |
| const char RewriteOptions::kDownstreamCachePurgeLocationPrefix[] = |
| "DownstreamCachePurgeLocationPrefix"; |
| const char RewriteOptions::kEnableFilters[] = "EnableFilters"; |
| const char RewriteOptions::kExperimentVariable[] = "ExperimentVariable"; |
| const char RewriteOptions::kExperimentSpec[] = "ExperimentSpec"; |
| const char RewriteOptions::kForbidFilters[] = "ForbidFilters"; |
| const char RewriteOptions::kInlineResourcesWithoutExplicitAuthorization[] = |
| "InlineResourcesWithoutExplicitAuthorization"; |
| const char RewriteOptions::kRetainComment[] = "RetainComment"; |
| const char RewriteOptions::kCustomFetchHeader[] = "CustomFetchHeader"; |
| const char RewriteOptions::kLoadFromFile[] = "LoadFromFile"; |
| const char RewriteOptions::kLoadFromFileMatch[] = "LoadFromFileMatch"; |
| const char RewriteOptions::kLoadFromFileRule[] = "LoadFromFileRule"; |
| const char RewriteOptions::kLoadFromFileRuleMatch[] = "LoadFromFileRuleMatch"; |
| const char RewriteOptions::kMapOriginDomain[] = "MapOriginDomain"; |
| const char RewriteOptions::kMapRewriteDomain[] = "MapRewriteDomain"; |
| const char RewriteOptions::kMapProxyDomain[] = "MapProxyDomain"; |
| const char RewriteOptions::kShardDomain[] = "ShardDomain"; |
| const char RewriteOptions::kUrlValuedAttribute[] = "UrlValuedAttribute"; |
| const char RewriteOptions::kLibrary[] = "Library"; |
| const char RewriteOptions::kCacheFlushFilename[] = "CacheFlushFilename"; |
| const char RewriteOptions::kCacheFlushPollIntervalSec[] = |
| "CacheFlushPollIntervalSec"; |
| const char RewriteOptions::kFetchHttps[] = "FetchHttps"; |
| const char RewriteOptions::kFetchFromModSpdy[] = "FetchFromModSpdy"; |
| const char RewriteOptions::kFetcherTimeOutMs[] = "FetcherTimeOutMs"; |
| const char RewriteOptions::kFileCacheCleanInodeLimit[] = |
| "FileCacheInodeLimit"; |
| const char RewriteOptions::kFileCacheCleanIntervalMs[] = |
| "FileCacheCleanIntervalMs"; |
| const char RewriteOptions::kFileCacheCleanSizeKb[] = "FileCacheSizeKb"; |
| const char RewriteOptions::kFileCachePath[] = "FileCachePath"; |
| const char RewriteOptions::kLogDir[] = "LogDir"; |
| const char RewriteOptions::kLruCacheByteLimit[] = "LRUCacheByteLimit"; |
| const char RewriteOptions::kLruCacheKbPerProcess[] = "LRUCacheKbPerProcess"; |
| const char RewriteOptions::kMemcachedServers[] = "MemcachedServers"; |
| const char RewriteOptions::kMemcachedThreads[] = "MemcachedThreads"; |
| const char RewriteOptions::kMemcachedTimeoutUs[] = "MemcachedTimeoutUs"; |
| const char RewriteOptions::kProxySuffix[] = "ProxySuffix"; |
| const char RewriteOptions::kRateLimitBackgroundFetches[] = |
| "RateLimitBackgroundFetches"; |
| const char RewriteOptions::kRemoteConfigurationUrl[] = "RemoteConfigurationUrl"; |
| const char RewriteOptions::kRemoteConfigurationTimeoutMs[] = |
| "RemoteConfigurationTimeoutMs"; |
| const char RewriteOptions::kRequestOptionOverride[] = "RequestOptionOverride"; |
| const char RewriteOptions::kServeWebpToAnyAgent[] = |
| "ServeRewrittenWebpUrlsToAnyAgent"; |
| const char RewriteOptions::kSlurpDirectory[] = "SlurpDirectory"; |
| const char RewriteOptions::kSlurpFlushLimit[] = "SlurpFlushLimit"; |
| const char RewriteOptions::kSlurpReadOnly[] = "SlurpReadOnly"; |
| const char RewriteOptions::kSslCertDirectory[] = "SslCertDirectory"; |
| const char RewriteOptions::kSslCertFile[] = "SslCertFile"; |
| const char RewriteOptions::kStatisticsEnabled[] = "Statistics"; |
| const char RewriteOptions::kStatisticsLoggingChartsCSS[] = |
| "StatisticsLoggingChartsCSS"; |
| const char RewriteOptions::kStatisticsLoggingChartsJS[] = |
| "StatisticsLoggingChartsJS"; |
| const char RewriteOptions::kStatisticsLoggingEnabled[] = |
| "StatisticsLogging"; |
| const char RewriteOptions::kStatisticsLoggingIntervalMs[] = |
| "StatisticsLoggingIntervalMs"; |
| const char RewriteOptions::kStatisticsLoggingMaxFileSizeKb[] = |
| "StatisticsLoggingMaxFileSizeKb"; |
| const char RewriteOptions::kTestProxy[] = "TestProxy"; |
| const char RewriteOptions::kTestProxySlurp[] = "TestProxySlurp"; |
| const char RewriteOptions::kUrlSigningKey[] = "UrlSigningKey"; |
| const char RewriteOptions::kUseAnalyticsJs[] = "UseAnalyticsJs"; |
| const char RewriteOptions::kUseSelectorsForCriticalCss[] = |
| "UseSelectorsForCriticalCss"; |
| const char RewriteOptions::kUseSharedMemLocking[] = "SharedMemoryLocks"; |
| const char RewriteOptions::kNullOption[] = ""; |
| |
| // RewriteFilter prefixes |
| const char RewriteOptions::kCacheExtenderId[] = "ce"; |
| const char RewriteOptions::kCollectFlushEarlyContentFilterId[] = "fe"; |
| const char RewriteOptions::kCssCombinerId[] = "cc"; |
| const char RewriteOptions::kCssFilterId[] = "cf"; |
| const char RewriteOptions::kCssImportFlattenerId[] = "if"; |
| const char RewriteOptions::kCssInlineId[] = "ci"; |
| const char RewriteOptions::kGoogleFontCssInlineId[] = "gf"; |
| const char RewriteOptions::kImageCombineId[] = "is"; |
| const char RewriteOptions::kImageCompressionId[] = "ic"; |
| const char RewriteOptions::kInPlaceRewriteId[] = "aj"; // Comes from ajax. |
| const char RewriteOptions::kJavascriptCombinerId[] = "jc"; |
| const char RewriteOptions::kJavascriptMinId[] = "jm"; |
| const char RewriteOptions::kJavascriptMinSourceMapId[] = "sm"; |
| const char RewriteOptions::kJavascriptInlineId[] = "ji"; |
| const char RewriteOptions::kLocalStorageCacheId[] = "ls"; |
| const char RewriteOptions::kPanelCommentPrefix[] = "GooglePanel"; |
| const char RewriteOptions::kPrioritizeCriticalCssId[] = "pr"; |
| |
| // Sets limit for buffering html in blink secondary fetch to 10MB default. |
| const int64 RewriteOptions::kDefaultBlinkMaxHtmlSizeRewritable = |
| 10 * 1024 * 1024; |
| |
| // TODO(jmarantz): consider merging this threshold with the image-inlining |
| // threshold, which is currently defaulting at 2000, so we have a single |
| // byte-count threshold, above which inlined resources get outlined, and |
| // below which outlined resources get inlined. |
| // |
| // TODO(jmarantz): user-agent-specific selection of inline threshold so that |
| // mobile phones are more prone to inlining. |
| // |
| // Further notes; jmaessen says: |
| // |
| // I suspect we do not want these bounds to match, and inlining for |
| // images is a bit more complicated because base64 encoding inflates |
| // the byte count of data: urls. This is a non-issue for other |
| // resources (there may be some weirdness with iframes I haven't |
| // thought about...). |
| // |
| // jmarantz says: |
| // |
| // One thing we could do, if we believe they should be conceptually |
| // merged, is in image_rewrite_filter you could apply the |
| // base64-bloat-factor before comparing against the threshold. Then |
| // we could use one number if we like that idea. |
| // |
| // jmaessen: For the moment, there's a separate threshold for image inline. |
| const int64 RewriteOptions::kDefaultCssInlineMaxBytes = 2048; |
| const int64 RewriteOptions::kDefaultCssFlattenMaxBytes = 1024000; |
| const int64 RewriteOptions::kDefaultCssImageInlineMaxBytes = 0; |
| const int64 RewriteOptions::kDefaultCssOutlineMinBytes = 3000; |
| // 3K is bigger than Roboto loader for Chrome (2.2k) |
| const int64 RewriteOptions::kDefaultGoogleFontCssInlineMaxBytes = 3 * 1024; |
| const int64 RewriteOptions::kDefaultImageInlineMaxBytes = 3072; |
| const int64 RewriteOptions::kDefaultJsInlineMaxBytes = 2048; |
| const int64 RewriteOptions::kDefaultJsOutlineMinBytes = 3000; |
| const int64 RewriteOptions::kDefaultProgressiveJpegMinBytes = 10240; |
| |
| const int64 RewriteOptions::kDefaultMaxHtmlCacheTimeMs = 0; |
| const int64 RewriteOptions::kDefaultMaxHtmlParseBytes = -1; |
| const int64 RewriteOptions::kDefaultMaxImageBytesForWebpInCss = kint64max; |
| |
| const int64 RewriteOptions::kDefaultMinResourceCacheTimeToRewriteMs = 0; |
| |
| const int64 RewriteOptions::kDefaultFlushBufferLimitBytes = 100 * 1024; |
| const int64 RewriteOptions::kDefaultIdleFlushTimeMs = 10; |
| const int64 RewriteOptions::kDefaultImplicitCacheTtlMs = 5 * Timer::kMinuteMs; |
| const int64 RewriteOptions::kDefaultLoadFromFileCacheTtlMs = |
| 5 * Timer::kMinuteMs; |
| const int64 RewriteOptions::kDefaultMinCacheTtlMs = -1; |
| const int64 RewriteOptions::kDefaultMetadataInputErrorsCacheTtlMs = |
| 5 * Timer::kMinuteMs; |
| |
| const int64 RewriteOptions::kDefaultPrioritizeVisibleContentCacheTimeMs = |
| 30 * Timer::kMinuteMs; // 30 mins. |
| |
| // Limit on concurrent ongoing image rewrites. |
| // TODO(jmaessen): Determine a sane default for this value. |
| const int RewriteOptions::kDefaultImageMaxRewritesAtOnce = 8; |
| |
| // IE limits URL size overall to about 2k characters. See |
| // http://support.microsoft.com/kb/208427/EN-US |
| const int RewriteOptions::kDefaultMaxUrlSize = 2083; |
| |
| // Quality that needs to be used while recompressing any image type. |
| // If set to -1, we use source image quality parameters, and is lossless. |
| const int64 RewriteOptions::kDefaultImageRecompressQuality = 85; |
| |
| // Jpeg quality that needs to be used while recompressing. If set to -1, we |
| // use the value of image_recompress_quality. |
| const int64 RewriteOptions::kDefaultImageJpegRecompressQuality = -1; |
| const int64 |
| RewriteOptions::kDefaultImageJpegRecompressQualityForSmallScreens = 70; |
| // TODO(huibao): Determine proper value for kDefaultImageJpegQualityForSaveData. |
| const int64 RewriteOptions::kDefaultImageJpegQualityForSaveData = 50; |
| |
| // Number of scans to output for jpeg images when using progressive mode. If set |
| // to -1, we retain all scans of a progressive jpeg. |
| const int64 RewriteOptions::kDefaultImageJpegNumProgressiveScans = -1; |
| |
| // Percentage savings in order to retain rewritten images; these default |
| // to 100% so that we always attempt to resize downsized images, and |
| // unconditionally retain images if they save any bytes at all. |
| const int RewriteOptions::kDefaultImageLimitOptimizedPercent = 100; |
| const int RewriteOptions::kDefaultImageLimitResizeAreaPercent = 100; |
| |
| // Percentage limit on image wxh reduction for the rendered dimensions to be |
| // stored in the property cache. This is kept at default 95 after |
| // some experiments." |
| const int RewriteOptions::kDefaultImageLimitRenderedAreaPercent = 95; |
| |
| // Sets limit for image optimization to 32MB. |
| const int64 RewriteOptions::kDefaultImageResolutionLimitBytes = 32*1024*1024; |
| |
| // WebP quality that needs to be used while recompressing. If set to -1, we |
| // use source image quality parameters. |
| const int64 RewriteOptions::kDefaultImageWebpRecompressQuality = 80; |
| const int64 |
| RewriteOptions::kDefaultImageWebpRecompressQualityForSmallScreens = 70; |
| const int64 RewriteOptions::kDefaultImageWebpAnimatedRecompressQuality = 70; |
| const int64 RewriteOptions::kDefaultImageWebpQualityForSaveData = 50; |
| |
| // Timeout, in ms, for all WebP conversion attempts for each source |
| // image. If negative, does not time out. |
| const int64 RewriteOptions::kDefaultImageWebpTimeoutMs = -1; |
| |
| // Setting the maximum length for the cacheable response content to -1 |
| // indicates that there is no size limit. |
| const int64 RewriteOptions::kDefaultMaxCacheableResponseContentLength = -1; |
| |
| // See http://code.google.com/p/modpagespeed/issues/detail?id=9. By |
| // default, Apache evidently limits each URL path segment (between /) |
| // to about 256 characters. This is not a fundamental URL limitation |
| // but is Apache specific. Ben Noordhuis has provided a workaround |
| // of hooking map_to_storage to skip the directory-mapping phase in |
| // Apache. See http://code.google.com/p/modpagespeed/issues/detail?id=176 |
| const int RewriteOptions::kDefaultMaxUrlSegmentSize = 1024; |
| |
| // Maximum JS elements to prefetch early when defer JS filter is enabled. |
| const int RewriteOptions::kDefaultMaxPrefetchJsElements = 0; |
| |
| // Expiration limit for cookies that set PageSpeed options: 10 minutes. |
| const int64 RewriteOptions::kDefaultOptionCookiesDurationMs = 10 * 60 * 1000; |
| |
| #ifdef NDEBUG |
| const int RewriteOptions::kDefaultRewriteDeadlineMs = 10; |
| #else |
| const int RewriteOptions::kDefaultRewriteDeadlineMs = 20; |
| #endif |
| const int kValgrindWaitForRewriteMs = 1000; |
| const int64 RewriteOptions::kDefaultDistributedTimeoutMs = 60000; |
| const int RewriteOptions::kDefaultPropertyCacheHttpStatusStabilityThreshold = 5; |
| |
| const int RewriteOptions::kDefaultMaxRewriteInfoLogSize = 150; |
| |
| const char RewriteOptions::kDefaultBeaconUrl[] = "/mod_pagespeed_beacon"; |
| |
| const int RewriteOptions::kDefaultMaxInlinedPreviewImagesIndex = -1; |
| const int64 RewriteOptions::kDefaultMinImageSizeLowResolutionBytes = 3 * 1024; |
| const int64 RewriteOptions::kDefaultMaxImageSizeLowResolutionBytes = |
| 1 * 1024 * 1024; // 1 MB. |
| |
| const int64 RewriteOptions::kDefaultMaxCombinedCssBytes = -1; // No size limit |
| // Setting the limit on combined js resource to -1 will bypass the size check. |
| const int64 RewriteOptions::kDefaultMaxCombinedJsBytes = 90 * 1024; |
| const int64 RewriteOptions::kDefaultExperimentCookieDurationMs = |
| Timer::kWeekMs; |
| const int64 RewriteOptions::kDefaultFinderPropertiesCacheExpirationTimeMs = |
| 2 * Timer::kHourMs; |
| const int64 RewriteOptions::kDefaultFinderPropertiesCacheRefreshTimeMs = |
| (3 * Timer::kHourMs) / 2; |
| const int64 RewriteOptions::kDefaultMetadataCacheStalenessThresholdMs = 0; |
| const char RewriteOptions::kDefaultDownstreamCachePurgeMethod[] = "PURGE"; |
| const int64 |
| RewriteOptions::kDefaultDownstreamCacheRewrittenPercentageThreshold = 95; |
| const int RewriteOptions::kDefaultExperimentTrafficPercent = 50; |
| const int RewriteOptions::kDefaultExperimentSlot = 1; |
| |
| // An empty default key indicates that the blocking rewrite feature is disabled. |
| const char RewriteOptions::kDefaultBlockingRewriteKey[] = ""; |
| |
| const char RewriteOptions::kRejectedRequestUrlKeyName[] = "RejectedUrl"; |
| |
| // Allow all the declared shards. |
| const int RewriteOptions::kDefaultDomainShardCount = 0; |
| |
| const int64 RewriteOptions::kDefaultBlinkHtmlChangeDetectionTimeMs = |
| Timer::kMinuteMs; |
| |
| // By default, rebeacon every 5 seconds in high frequency mode. This will be |
| // multiplied by kLowFreqBeaconMult in critical_finder_support_util.h to |
| // determine the low frequency rebeacon time. |
| const int RewriteOptions::kDefaultBeaconReinstrumentTimeSec = 5; |
| |
| // By default, all images are inline-previewed irrespective of size. |
| const int64 RewriteOptions::kDefaultMaxLowResImageSizeBytes = -1; |
| |
| // By default, all images are inline-previewed, as long as the low-res size is |
| // lesser than the full-res size. |
| const int RewriteOptions::kDefaultMaxLowResToFullResImageSizePercentage = 100; |
| |
| const double RewriteOptions::kDefaultResponsiveImageDensities[] = |
| { 1.5, 2.0, 3.0 }; |
| |
| const RewriteOptions::FilterEnumToIdAndNameEntry* |
| RewriteOptions::filter_id_to_enum_array_[RewriteOptions::kEndOfFilters]; |
| |
| RewriteOptions::PropertyNameMap* |
| RewriteOptions::option_name_to_property_map_ = NULL; |
| |
| const RewriteOptions::PropertyBase** |
| RewriteOptions::option_id_to_property_array_ = NULL; |
| |
| RewriteOptions::Properties* RewriteOptions::properties_ = NULL; |
| RewriteOptions::Properties* RewriteOptions::all_properties_ = NULL; |
| |
| const char RewriteOptions::AllowVaryOn::kNoneString[] = "None"; |
| const char RewriteOptions::AllowVaryOn::kAutoString[] = "Auto"; |
| |
| namespace { |
| |
| // When you change this, remember to update the documentation: |
| // doc/en/speed/pagespeed/module/config_filters.html |
| // The documentation there includes the filter groups |
| // "rewrite_images", "extend_cache", and "rewrite_javascript", which |
| // expand to multiple filters, all of which need to be listed here. |
| // config_filters.html both includes lists of filters in each group |
| // and, redundantly, a table of all filters with one-liner |
| // documentation and which groups they are in. |
| const RewriteOptions::Filter kCoreFilterSet[] = { |
| RewriteOptions::kAddHead, |
| RewriteOptions::kCombineCss, |
| RewriteOptions::kCombineJavascript, |
| RewriteOptions::kConvertGifToPng, // rewrite_images |
| RewriteOptions::kConvertJpegToProgressive, // rewrite_images |
| RewriteOptions::kConvertJpegToWebp, // rewrite_images |
| RewriteOptions::kConvertMetaTags, |
| RewriteOptions::kConvertPngToJpeg, // rewrite_images |
| RewriteOptions::kConvertToWebpLossless, // rewrite_images |
| RewriteOptions::kExtendCacheCss, // extend_cache |
| RewriteOptions::kExtendCacheImages, // extend_cache |
| RewriteOptions::kExtendCacheScripts, // extend_cache |
| RewriteOptions::kFallbackRewriteCssUrls, |
| RewriteOptions::kFlattenCssImports, |
| RewriteOptions::kInlineCss, |
| RewriteOptions::kInlineImages, // rewrite_images |
| RewriteOptions::kInlineImportToLink, |
| RewriteOptions::kInlineJavascript, |
| RewriteOptions::kJpegSubsampling, // rewrite_images |
| RewriteOptions::kRecompressJpeg, // rewrite_images |
| RewriteOptions::kRecompressPng, // rewrite_images |
| RewriteOptions::kRecompressWebp, // rewrite_images |
| RewriteOptions::kResizeImages, // rewrite_images |
| RewriteOptions::kRewriteCss, |
| RewriteOptions::kRewriteJavascriptExternal, // rewrite_javascript |
| RewriteOptions::kRewriteJavascriptInline, // rewrite_javascript |
| RewriteOptions::kRewriteStyleAttributesWithUrl, |
| RewriteOptions::kStripImageColorProfile, // rewrite_images |
| RewriteOptions::kStripImageMetaData, // rewrite_images |
| }; |
| |
| // The bandwidth-reduction filters exclude any filter that may modify |
| // URLs (combine, cache-extend, inline, outline). Note also that turning |
| // on this level enables "preserve" mode which has the effect of |
| // making combine_css et al turn itself off. |
| // |
| // When you change this, remember to update the documentation: |
| // doc/en/speed/pagespeed/module/config_filters.html |
| // The documentation there includes the filter groups "rewrite_images" and |
| // "extend_cache" which expand to multiple filters, all of which need to be |
| // listed here. config_filters.html both includes lists of filters in each |
| // group and, redundantly, a table of all filters with one-liner documentation |
| // and which groups they are in. |
| const RewriteOptions::Filter kOptimizeForBandwidthFilterSet[] = { |
| RewriteOptions::kConvertGifToPng, // rewrite_images |
| RewriteOptions::kConvertJpegToProgressive, // rewrite_images |
| RewriteOptions::kConvertJpegToWebp, // rewrite_images |
| RewriteOptions::kConvertPngToJpeg, // rewrite_images |
| RewriteOptions::kInPlaceOptimizeForBrowser, |
| RewriteOptions::kJpegSubsampling, // rewrite_images |
| RewriteOptions::kRecompressJpeg, // rewrite_images |
| RewriteOptions::kRecompressPng, // rewrite_images |
| RewriteOptions::kRecompressWebp, // rewrite_images |
| RewriteOptions::kRewriteCss, |
| RewriteOptions::kRewriteJavascriptExternal, // rewrite_javascript |
| RewriteOptions::kRewriteJavascriptInline, // rewrite_javascript |
| RewriteOptions::kStripImageColorProfile, // rewrite_images |
| RewriteOptions::kStripImageMetaData, // rewrite_images |
| }; |
| |
| // Note: all Core filters are Test filters as well. For maintainability, |
| // this is managed in the c++ switch statement. |
| const RewriteOptions::Filter kTestFilterSet[] = { |
| RewriteOptions::kConvertJpegToWebp, |
| RewriteOptions::kDebug, |
| RewriteOptions::kDeferIframe, |
| RewriteOptions::kDeferJavascript, |
| RewriteOptions::kDelayImages, // AKA inline_preview_images |
| RewriteOptions::kIncludeJsSourceMaps, |
| RewriteOptions::kInsertGA, |
| RewriteOptions::kInsertImageDimensions, |
| RewriteOptions::kLazyloadImages, |
| RewriteOptions::kLeftTrimUrls, |
| RewriteOptions::kMakeGoogleAnalyticsAsync, |
| RewriteOptions::kPrioritizeCriticalCss, |
| RewriteOptions::kResizeToRenderedImageDimensions, |
| RewriteOptions::kResponsiveImages, |
| RewriteOptions::kRewriteDomains, |
| RewriteOptions::kSpriteImages, |
| }; |
| |
| // Note: These filters should not be included even if the level is "All". |
| const RewriteOptions::Filter kDangerousFilterSet[] = { |
| RewriteOptions::kCachePartialHtml, |
| RewriteOptions::kCanonicalizeJavascriptLibraries, |
| RewriteOptions::kComputeVisibleText, // internal, enabled conditionally |
| RewriteOptions::kDeterministicJs, // used for measurement |
| RewriteOptions::kDisableJavascript, |
| RewriteOptions::kDivStructure, |
| RewriteOptions::kExperimentCollectMobImageInfo, |
| RewriteOptions::kExperimentSpdy, |
| RewriteOptions::kExplicitCloseTags, |
| RewriteOptions::kFixReflows, |
| RewriteOptions::kMobilize, // Prototype |
| RewriteOptions::kMobilizePrecompute, // TODO(jud): Unused, remove. |
| RewriteOptions::kServeDeprecationNotice, // internal. |
| RewriteOptions::kSplitHtml, // internal, enabled conditionally |
| RewriteOptions::kSplitHtmlHelper, // internal, enabled conditionally |
| RewriteOptions::kStripNonCacheable, // internal, enabled conditionally |
| RewriteOptions::kStripScripts, |
| }; |
| |
| // List of filters whose correct behavior requires script execution. |
| // NOTE: Modify the |
| // SupportNoscriptFilter::IsAnyFilterRequiringScriptExecutionEnabled() method |
| // if you update this list. |
| const RewriteOptions::Filter kRequiresScriptExecutionFilterSet[] = { |
| RewriteOptions::kCachePartialHtml, |
| RewriteOptions::kDedupInlinedImages, |
| RewriteOptions::kDeferIframe, |
| RewriteOptions::kDeferJavascript, |
| RewriteOptions::kDelayImages, |
| RewriteOptions::kFlushSubresources, |
| RewriteOptions::kLazyloadImages, |
| RewriteOptions::kLocalStorageCache, |
| RewriteOptions::kMobilize, |
| RewriteOptions::kSplitHtml, |
| // We do not include kPrioritizeVisibleContent since we do not want to attach |
| // SupportNoscriptFilter in the case of blink pcache miss pass-through, since |
| // this response will not have any custom script inserted. |
| // Do the various critical css filters belong here? Arguably not, since even |
| // if we transform a page based on beacon results we'll enclose the necessary |
| // in a noscript block and the page will still load / function normally. |
| }; |
| |
| // List of filters which are essential for mobilizing webpages, i.e., for |
| // making webpages designed for desktop computers look good on mobile devices. |
| // |
| // TODO(huibao): Once rewrite levels can be combined, move kRewriteCss and |
| // kRewriteDomains (and kDomainRewriteHyperlinks Option) into a proxy mode. |
| const RewriteOptions::Filter kMobilizeFilterSet[] = { |
| // TODO(huibao): When layout mode is enabled, uncomment kInlineJavascript |
| // to inline xhr.js, a small JS file which we create, into HTML. |
| // RewriteOptions::kInlineJavascript, |
| RewriteOptions::kMobilize, |
| // Turn on rewrite_css in order to rewrite hyper-links in CSS. |
| RewriteOptions::kRewriteCss, |
| RewriteOptions::kRewriteDomains, |
| }; |
| |
| // Array of mappings from Filter enum to corresponding filter id and name, |
| // used to map an enum value to id/name, and also used to initialize the |
| // reverse map from id to enum. Although the filter_enum field is not strictly |
| // necessary (because it equals the entry's index in the array), it is here so |
| // we can check during initialization that the array has been set up correctly. |
| // |
| // MUST be updated whenever a new Filter value is added and the new entry |
| // MUST be inserted in Filter enum order. |
| const RewriteOptions::FilterEnumToIdAndNameEntry |
| kFilterVectorStaticInitializer[] = { |
| {RewriteOptions::kAddBaseTag, "ab", "Add Base Tag"}, |
| {RewriteOptions::kAddHead, "ah", "Add Head"}, |
| {RewriteOptions::kAddIds, "ad", "Add Ids"}, |
| {RewriteOptions::kAddInstrumentation, "ai", "Add Instrumentation"}, |
| {RewriteOptions::kComputeStatistics, "ca", "Compute HTML statistics"}, |
| {RewriteOptions::kCachePartialHtml, "ct", "Cache Partial Html"}, |
| {RewriteOptions::kCanonicalizeJavascriptLibraries, "ij", |
| "Canonicalize Javascript library URLs"}, |
| {RewriteOptions::kCollapseWhitespace, "cw", "Collapse Whitespace"}, |
| {RewriteOptions::kCollectFlushEarlyContentFilter, |
| RewriteOptions::kCollectFlushEarlyContentFilterId, |
| "Collect Flush Early Content Filter"}, |
| {RewriteOptions::kCombineCss, RewriteOptions::kCssCombinerId, |
| "Combine Css"}, |
| {RewriteOptions::kCombineHeads, "ch", "Combine Heads"}, |
| {RewriteOptions::kCombineJavascript, |
| RewriteOptions::kJavascriptCombinerId, "Combine Javascript"}, |
| {RewriteOptions::kComputeCriticalCss, "bc", |
| "Background Compute Critical css"}, |
| {RewriteOptions::kComputeVisibleText, "bp", "Computes visible text"}, |
| {RewriteOptions::kConvertGifToPng, "gp", "Convert Gif to Png"}, |
| {RewriteOptions::kConvertJpegToProgressive, "jp", |
| "Convert Jpeg to Progressive"}, |
| {RewriteOptions::kConvertJpegToWebp, "jw", "Convert Jpeg To Webp"}, |
| {RewriteOptions::kConvertMetaTags, "mc", "Convert Meta Tags"}, |
| {RewriteOptions::kConvertPngToJpeg, "pj", "Convert Png to Jpeg"}, |
| { RewriteOptions::kConvertToWebpAnimated, "wa", |
| "Convert animated images to WebP" }, |
| {RewriteOptions::kConvertToWebpLossless, "ws", |
| "When converting images to WebP, prefer lossless conversions"}, |
| {RewriteOptions::kDebug, "db", "Debug"}, |
| {RewriteOptions::kDecodeRewrittenUrls, "du", "Decode Rewritten URLs"}, |
| {RewriteOptions::kDedupInlinedImages, "dd", "Dedup Inlined Images"}, |
| {RewriteOptions::kDeferIframe, "df", "Defer Iframe"}, |
| {RewriteOptions::kDeferJavascript, "dj", "Defer Javascript"}, |
| {RewriteOptions::kDelayImages, "di", "Delay Images"}, |
| {RewriteOptions::kDeterministicJs, "mj", "Deterministic Js"}, |
| {RewriteOptions::kDisableJavascript, "jd", |
| "Disables scripts by placing them inside noscript tags"}, |
| {RewriteOptions::kDivStructure, "ds", "Div Structure"}, |
| {RewriteOptions::kElideAttributes, "ea", "Elide Attributes"}, |
| {RewriteOptions::kExperimentCollectMobImageInfo, "xi", |
| "Experiment: collect image info to help mobilization"}, |
| {RewriteOptions::kExperimentSpdy, "xs", "SPDY Resources Experiment"}, |
| {RewriteOptions::kExplicitCloseTags, "xc", "Explicit Close Tags"}, |
| {RewriteOptions::kExtendCacheCss, "ec", "Cache Extend Css"}, |
| {RewriteOptions::kExtendCacheImages, "ei", "Cache Extend Images"}, |
| {RewriteOptions::kExtendCachePdfs, "ep", "Cache Extend PDFs"}, |
| {RewriteOptions::kExtendCacheScripts, "es", "Cache Extend Scripts"}, |
| {RewriteOptions::kFallbackRewriteCssUrls, "fc", |
| "Fallback Rewrite Css "}, |
| {RewriteOptions::kFixReflows, "fr", "Fix Reflows"}, |
| {RewriteOptions::kFlattenCssImports, |
| RewriteOptions::kCssImportFlattenerId, "Flatten CSS Imports"}, |
| {RewriteOptions::kFlushSubresources, "fs", "Flush Subresources"}, |
| {RewriteOptions::kHandleNoscriptRedirect, "hn", |
| "Handles Noscript Redirects"}, |
| {RewriteOptions::kHtmlWriterFilter, "hw", "Flushes html"}, |
| {RewriteOptions::kIncludeJsSourceMaps, |
| RewriteOptions::kJavascriptMinSourceMapId, "Include JS Source Maps"}, |
| {RewriteOptions::kInlineCss, RewriteOptions::kCssInlineId, |
| "Inline Css"}, |
| {RewriteOptions::kInlineGoogleFontCss, |
| RewriteOptions::kGoogleFontCssInlineId, "Inline Google Font CSS"}, |
| {RewriteOptions::kInlineImages, "ii", "Inline Images"}, |
| {RewriteOptions::kInlineImportToLink, "il", "Inline @import to Link"}, |
| {RewriteOptions::kInlineJavascript, RewriteOptions::kJavascriptInlineId, |
| "Inline Javascript"}, |
| {RewriteOptions::kInPlaceOptimizeForBrowser, "io", |
| "In-place optimize for browser"}, |
| {RewriteOptions::kInsertDnsPrefetch, "idp", "Insert DNS Prefetch"}, |
| {RewriteOptions::kInsertGA, "ig", "Insert Google Analytics"}, |
| {RewriteOptions::kInsertImageDimensions, "id", |
| "Insert Image Dimensions"}, |
| {RewriteOptions::kJpegSubsampling, "js", "Jpeg Subsampling"}, |
| {RewriteOptions::kLazyloadImages, "ll", "Lazyload Images"}, |
| {RewriteOptions::kLeftTrimUrls, "tu", "Left Trim Urls"}, |
| {RewriteOptions::kLocalStorageCache, |
| RewriteOptions::kLocalStorageCacheId, "Local Storage Cache"}, |
| {RewriteOptions::kMakeGoogleAnalyticsAsync, "ga", |
| "Make Google Analytics Async"}, |
| {RewriteOptions::kMakeShowAdsAsync, "gaa", |
| "Convert showads.js use to async adsbygoogle.js"}, |
| {RewriteOptions::kMobilize, "mob", "Mobilize Webpage"}, |
| {RewriteOptions::kMobilizePrecompute, "mob_precompute", "Deprecated."}, |
| {RewriteOptions::kMoveCssAboveScripts, "cj", "Move Css Above Scripts"}, |
| {RewriteOptions::kMoveCssToHead, "cm", "Move Css To Head"}, |
| {RewriteOptions::kOutlineCss, "co", "Outline Css"}, |
| {RewriteOptions::kOutlineJavascript, "jo", "Outline Javascript"}, |
| {RewriteOptions::kPedantic, "pc", "Add pedantic types"}, |
| {RewriteOptions::kPrioritizeCriticalCss, |
| RewriteOptions::kPrioritizeCriticalCssId, "Prioritize Critical Css"}, |
| {RewriteOptions::kRecompressJpeg, "rj", "Recompress Jpeg"}, |
| {RewriteOptions::kRecompressPng, "rp", "Recompress Png"}, |
| {RewriteOptions::kRecompressWebp, "rw", "Recompress Webp"}, |
| {RewriteOptions::kRemoveComments, "rc", "Remove Comments"}, |
| {RewriteOptions::kRemoveQuotes, "rq", "Remove Quotes"}, |
| {RewriteOptions::kResizeImages, "ri", "Resize Images"}, |
| {RewriteOptions::kResizeMobileImages, "rm", "Resize Mobile Images"}, |
| {RewriteOptions::kResizeToRenderedImageDimensions, "ir", |
| "Resize to Rendered Image Dimensions"}, |
| {RewriteOptions::kResponsiveImages, "rx", "Responsive Images"}, |
| {RewriteOptions::kResponsiveImagesZoom, "rz", "Responsive Images Zoom"}, |
| {RewriteOptions::kRewriteCss, RewriteOptions::kCssFilterId, |
| "Rewrite Css"}, |
| {RewriteOptions::kRewriteDomains, "rd", "Rewrite Domains"}, |
| {RewriteOptions::kRewriteJavascriptExternal, |
| RewriteOptions::kJavascriptMinId, "Rewrite External Javascript"}, |
| {RewriteOptions::kRewriteJavascriptInline, "jj", |
| "Rewrite Inline Javascript"}, |
| {RewriteOptions::kRewriteStyleAttributes, "cs", |
| "Rewrite Style Attributes"}, |
| {RewriteOptions::kRewriteStyleAttributesWithUrl, "cu", |
| "Rewrite Style Attributes With Url"}, |
| {RewriteOptions::kServeDeprecationNotice, "sd", |
| "Serve Deprecation Notice"}, |
| {RewriteOptions::kSplitHtml, "sh", "Split Html"}, |
| {RewriteOptions::kSplitHtmlHelper, "se", "Split Html Helper"}, |
| {RewriteOptions::kSpriteImages, RewriteOptions::kImageCombineId, |
| "Sprite Images"}, |
| {RewriteOptions::kStripImageColorProfile, "cp", |
| "Strip Image Color Profiles"}, |
| {RewriteOptions::kStripImageMetaData, "md", "Strip Image Meta Data"}, |
| {RewriteOptions::kStripNonCacheable, "nc", "Strip Non Cacheable"}, |
| {RewriteOptions::kStripScripts, "ss", "Strip Scripts"}, |
| }; |
| |
| const RewriteOptions::Filter kImagePreserveUrlDisabledFilters[] = { |
| // TODO(jkarlin): Remove kResizeImages from the forbid list and allow image |
| // squashing prefetching in HTML path (but don't allow resizing based on |
| // HTML attributes). |
| RewriteOptions::kDelayImages, |
| RewriteOptions::kExtendCacheImages, |
| RewriteOptions::kInlineImages, |
| RewriteOptions::kLazyloadImages, |
| RewriteOptions::kResizeImages, |
| RewriteOptions::kResizeToRenderedImageDimensions, |
| RewriteOptions::kResponsiveImages, |
| RewriteOptions::kSpriteImages |
| }; |
| |
| const RewriteOptions::Filter kJsPreserveUrlDisabledFilters[] = { |
| RewriteOptions::kCanonicalizeJavascriptLibraries, |
| RewriteOptions::kCombineJavascript, |
| RewriteOptions::kDeferJavascript, |
| RewriteOptions::kExtendCacheScripts, |
| RewriteOptions::kInlineJavascript, |
| RewriteOptions::kOutlineJavascript |
| }; |
| |
| const RewriteOptions::Filter kCssPreserveUrlDisabledFilters[] = { |
| RewriteOptions::kCombineCss, |
| RewriteOptions::kExtendCacheCss, |
| RewriteOptions::kInlineCss, |
| RewriteOptions::kInlineGoogleFontCss, |
| RewriteOptions::kInlineImportToLink, |
| RewriteOptions::kLeftTrimUrls, |
| RewriteOptions::kOutlineCss |
| }; |
| |
| #ifndef NDEBUG |
| void CheckFilterSetOrdering(const RewriteOptions::Filter* filters, int num) { |
| for (int i = 1; i < num; ++i) { |
| DCHECK_GT(filters[i], filters[i - 1]); |
| } |
| } |
| #endif |
| |
| // Table of properties for each filter to make it faster to check whether the |
| // a filter is a member of a rewrite level or needs to be disabled when a |
| // configuration is set to preserve resource URLs. The table is initialized |
| // once in RewriteOptions::Initialize. |
| struct FilterProperties { |
| uint8 level_core : 1; |
| uint8 level_optimize_for_bandwidth : 1; |
| uint8 level_mobilize : 1; |
| uint8 level_test : 1; |
| uint8 level_dangerous : 1; |
| uint8 preserve_image_urls : 1; |
| uint8 preserve_js_urls : 1; |
| uint8 preserve_css_urls : 1; |
| }; |
| FilterProperties filter_properties[RewriteOptions::kEndOfFilters]; |
| |
| bool IsInSet(const RewriteOptions::Filter* filters, int num, |
| RewriteOptions::Filter filter) { |
| const RewriteOptions::Filter* end = filters + num; |
| return std::binary_search(filters, end, filter); |
| } |
| |
| // Strips the "ets=" query param (if present) from the end of url and strips all |
| // query params from url and assigns to url_no_query_param. |
| void StripBeaconUrlQueryParam(GoogleString* url, |
| GoogleString* url_no_query_param) { |
| if (StringPiece(*url).ends_with("ets=")) { |
| // Strip the ? or & in front of ets= as well. |
| int chars_to_strip = STATIC_STRLEN("ets=") + 1; |
| url->resize(url->size() - chars_to_strip); |
| } |
| |
| StringPieceVector url_split; |
| SplitStringUsingSubstr(*url, "?", &url_split); |
| url_split[0].CopyToString(url_no_query_param); |
| } |
| |
| // Maps the deprecated options to the new names. |
| struct DeprecatedOptionMap { |
| static bool LessThan( |
| const DeprecatedOptionMap& option_map, |
| StringPiece arg) { |
| return StringCaseCompare(option_map.deprecated_option_name, arg) < 0; |
| } |
| |
| const char* deprecated_option_name; |
| const char* new_option_name; |
| }; |
| |
| const DeprecatedOptionMap kDeprecatedOptionNameData[] = { |
| {"ImageWebpRecompressionQuality", |
| "WebpRecompressionQuality"}, |
| {"ImageWebpRecompressionQualityForSmallScreens", |
| "WebpRecompressionQualityForSmallScreens"} |
| }; |
| |
| std::vector<DeprecatedOptionMap> kDeprecatedOptionNameList( |
| kDeprecatedOptionNameData, |
| kDeprecatedOptionNameData + arraysize(kDeprecatedOptionNameData) |
| ); |
| |
| } // namespace |
| |
| const char* RewriteOptions::FilterName(Filter filter) { |
| int i = static_cast<int>(filter); |
| int n = arraysize(kFilterVectorStaticInitializer); |
| if (i >= 0 && i < n) { |
| return kFilterVectorStaticInitializer[i].filter_name; |
| } |
| LOG(DFATAL) << "Unknown filter: " << filter; |
| return "Unknown Filter"; |
| } |
| |
| const char* RewriteOptions::FilterId(Filter filter) { |
| int i = static_cast<int>(filter); |
| int n = arraysize(kFilterVectorStaticInitializer); |
| if (i >= 0 && i < n) { |
| return kFilterVectorStaticInitializer[i].filter_id; |
| } |
| LOG(DFATAL) << "Unknown filter code: " << filter; |
| return "UF"; |
| } |
| |
| int RewriteOptions::NumFilterIds() { |
| return arraysize(kFilterVectorStaticInitializer); |
| } |
| |
| bool RewriteOptions::ParseRewriteLevel( |
| const StringPiece& in, RewriteLevel* out) { |
| bool ret = false; |
| if (in != NULL) { |
| if (StringCaseEqual(in, "CoreFilters")) { |
| *out = kCoreFilters; |
| ret = true; |
| } else if (StringCaseEqual(in, "PassThrough")) { |
| *out = kPassThrough; |
| ret = true; |
| } else if (StringCaseEqual(in, "OptimizeForBandwidth")) { |
| *out = kOptimizeForBandwidth; |
| ret = true; |
| } else if (StringCaseEqual(in, "MobilizeFilters")) { |
| *out = kMobilizeFilters; |
| ret = true; |
| } else if (StringCaseEqual(in, "TestingCoreFilters")) { |
| *out = kTestingCoreFilters; |
| ret = true; |
| } else if (StringCaseEqual(in, "AllFilters")) { |
| *out = kAllFilters; |
| ret = true; |
| } |
| } |
| return ret; |
| } |
| |
| bool RewriteOptions::ParseInlineUnauthorizedResourceType( |
| const StringPiece& in, |
| ResourceCategorySet* out) { |
| // Examples: |
| // InlineResourcesWithoutExplicitAuthorization Script,Stylesheet |
| // InlineResourcesWithoutExplicitAuthorization Stylesheet |
| // InlineResourcesWithoutExplicitAuthorization off |
| StringPieceVector resource_types; |
| SplitStringPieceToVector(in, ",", &resource_types, true); |
| for (int i = 0, n = resource_types.size(); i < n; ++i) { |
| StringPiece resource_type = resource_types[i]; |
| semantic_type::Category category; |
| if (StringCaseEqual(resource_type, "off")) { |
| out->clear(); |
| } else if (!semantic_type::ParseCategory(resource_type, &category)) { |
| // Invalid resource category. |
| return false; |
| } else { |
| out->insert(category); |
| } |
| } |
| return true; |
| } |
| |
| bool RewriteOptions::ParseBeaconUrl(const StringPiece& in, BeaconUrl* out) { |
| StringPieceVector urls; |
| SplitStringPieceToVector(in, " ", &urls, true); |
| |
| if (urls.size() > 2 || urls.size() < 1) { |
| return false; |
| } |
| urls[0].CopyToString(&out->http); |
| if (urls.size() == 2) { |
| urls[1].CopyToString(&out->https); |
| } else if (urls[0].starts_with("http:")) { |
| out->https.clear(); |
| StrAppend(&out->https, "https:", urls[0].substr(STATIC_STRLEN("http:"))); |
| } else { |
| urls[0].CopyToString(&out->https); |
| } |
| |
| // We used to require that the query param end with "ets=", but no longer |
| // do, so strip it if it's present. We also assign http_in and https_in to the |
| // beacon URL stripped of their query params, if any are present. |
| StripBeaconUrlQueryParam(&out->http, &out->http_in); |
| StripBeaconUrlQueryParam(&out->https, &out->https_in); |
| |
| return true; |
| } |
| |
| bool RewriteOptions::ParseFromString(StringPiece in, Color* color) { |
| // We just handle #aabbcc syntax. |
| if (in.length() != 7 || in[0] != '#') { |
| return false; |
| } |
| for (int i = 1; i < 7; ++i) { |
| if (!IsHexDigit(in[i])) { |
| return false; |
| } |
| } |
| uint32 r = 0, g = 0, b = 0; |
| AccumulateHexValue(in[1], &r); |
| AccumulateHexValue(in[2], &r); |
| AccumulateHexValue(in[3], &g); |
| AccumulateHexValue(in[4], &g); |
| AccumulateHexValue(in[5], &b); |
| AccumulateHexValue(in[6], &b); |
| color->r = static_cast<unsigned char>(r); |
| color->g = static_cast<unsigned char>(g); |
| color->b = static_cast<unsigned char>(b); |
| return true; |
| } |
| |
| bool RewriteOptions::ParseFromString(StringPiece in, MobTheme* theme) { |
| StringPieceVector args; |
| SplitStringPieceToVector(in, " ", &args, true); |
| if (args.size() != 2 && args.size() != 3) { |
| return false; |
| } |
| |
| if (!ParseFromString(args[0], &theme->background_color) || |
| !ParseFromString(args[1], &theme->foreground_color)) { |
| return false; |
| } |
| if (args.size() == 3) { |
| args[2].CopyToString(&theme->logo_url); |
| } |
| return true; |
| } |
| |
| bool RewriteOptions::ImageOptimizationEnabled() const { |
| return (this->Enabled(RewriteOptions::kRecompressJpeg) || |
| this->Enabled(RewriteOptions::kRecompressPng) || |
| this->Enabled(RewriteOptions::kRecompressWebp) || |
| this->Enabled(RewriteOptions::kConvertGifToPng) || |
| this->Enabled(RewriteOptions::kConvertJpegToProgressive) || |
| this->Enabled(RewriteOptions::kConvertPngToJpeg) || |
| this->Enabled(RewriteOptions::kConvertJpegToWebp) || |
| this->Enabled(RewriteOptions::kConvertToWebpAnimated) || |
| this->Enabled(RewriteOptions::kConvertToWebpLossless)); |
| } |
| |
| RewriteOptions::RewriteOptions(ThreadSystem* thread_system) |
| : modified_(false), |
| frozen_(false), |
| purge_set_(PurgeSet(kCachePurgeBytes)), |
| initialized_options_(0), |
| options_uniqueness_checked_(false), |
| need_to_store_experiment_data_(false), |
| experiment_id_(experiment::kExperimentNotSet), |
| experiment_percent_(0), |
| signature_(), |
| hasher_(kHashBytes), |
| thread_system_(thread_system) { |
| cache_purge_mutex_.reset(new NullRWLock); |
| |
| DCHECK(properties_ != NULL) |
| << "Call RewriteOptions::Initialize() before construction"; |
| |
| // Sanity-checks -- will be active only when compiled for debug. |
| #ifndef NDEBUG |
| CheckFilterSetOrdering(kCoreFilterSet, arraysize(kCoreFilterSet)); |
| CheckFilterSetOrdering(kTestFilterSet, arraysize(kTestFilterSet)); |
| CheckFilterSetOrdering(kDangerousFilterSet, arraysize(kDangerousFilterSet)); |
| CheckFilterSetOrdering(kImagePreserveUrlDisabledFilters, |
| arraysize(kImagePreserveUrlDisabledFilters)); |
| CheckFilterSetOrdering(kJsPreserveUrlDisabledFilters, |
| arraysize(kJsPreserveUrlDisabledFilters)); |
| CheckFilterSetOrdering(kCssPreserveUrlDisabledFilters, |
| arraysize(kCssPreserveUrlDisabledFilters)); |
| |
| // Ensure that all filters have unique IDs. |
| StringSet id_set; |
| for (int i = 0; i < static_cast<int>(kEndOfFilters); ++i) { |
| Filter filter = static_cast<Filter>(i); |
| const char* id = FilterId(filter); |
| std::pair<StringSet::iterator, bool> insertion = id_set.insert(id); |
| DCHECK(insertion.second) << "Duplicate RewriteOption filter id: " << id; |
| } |
| |
| // We can't check options uniqueness until additional extra |
| // options are added by subclasses. We could do this in the |
| // destructor I suppose, but we defer it till ComputeSignature. |
| #endif |
| |
| // TODO(jmarantz): make rewrite_deadline changeable from the Factory based on |
| // the requirements of the testing system and the platform. This might also |
| // want to change based on how many Flushes there are, as each Flush can |
| // potentially add this much more latency. |
| if (RunningOnValgrind()) { |
| set_rewrite_deadline_ms(kValgrindWaitForRewriteMs); |
| set_in_place_rewrite_deadline_ms(kValgrindWaitForRewriteMs); |
| modified_ = false; |
| #ifndef NDEBUG |
| last_thread_id_.reset(); |
| #endif |
| } |
| |
| InitializeOptions(properties_); |
| |
| // Enable HtmlWriterFilter by default. |
| EnableFilter(kHtmlWriterFilter); |
| } |
| |
| // static |
| void RewriteOptions::AddProperties() { |
| // TODO(jmarantz): move the help text to #defines or maybe const char[] in |
| // .h file so that rewrite_gflags.cc can reference the same strings in |
| // DEFINE_xxx directives. |
| // |
| // |
| // Note: there are two functions used for registering properties here, |
| // AddBaseProperty() and AddRequestProperty(). AddRequestProperty() |
| // is kind of a hack for stuffing request-specific data into the RewriteOption |
| // object. Those options should probably be changed to be fields in the |
| // recently-added RequestContext. |
| // |
| // AddBaseProperty() is for user-settable options. The last argument |
| // is a help-string. The presence of a help-string enables the option |
| // for mod_pagespeed, and serves as the error message if there is a |
| // syntax error specifying the option in pagespeed.conf. |
| // |
| // There are three sorts of options which pass in NULL for the help-string |
| // 1. Options that should be enabled in mod_pagespeed but we haven't |
| // written the help-string or added HTML documentation yet. These |
| // will be flagged with: |
| // // TODO(jmarantz): write help & doc for mod_pagespeed. |
| // 2. Options which are experimental and temporary and are not ready for |
| // permanent support in mod_pagespeed. These will be marked: |
| // // TODO(jmarantz): eliminate experiment or document. |
| // 3. Options which are not applicable to mod_pagespeed, e.g. those that |
| // support features not yet in mod_pagespeed such as Blink, or have |
| // an alternate solution (populating the cache invalidation timestamp). |
| // These are marked as: |
| // // Not applicable for mod_pagespeed. |
| // 4. Options which should be in mod_pagespeed but need a bit more |
| // implementation before they are ready. Marked as: |
| // // TODO(jmarantz): implement for mod_pagespeed. |
| AddBaseProperty( |
| kPassThrough, &RewriteOptions::level_, "l", kRewriteLevel, |
| kDirectoryScope, |
| "Base level of rewriting (PassThrough, CoreFilters)", true); |
| AddBaseProperty( |
| kDefaultBlinkMaxHtmlSizeRewritable, |
| &RewriteOptions::blink_max_html_size_rewritable_, |
| "bmhsr", kBlinkMaxHtmlSizeRewritable, |
| kDirectoryScope, |
| NULL, true); // Not applicable for mod_pagespeed. |
| AddBaseProperty( |
| kDefaultCssFlattenMaxBytes, |
| &RewriteOptions::css_flatten_max_bytes_, "cf", |
| kCssFlattenMaxBytes, |
| kQueryScope, |
| "Number of bytes below which stylesheets will be flattened.", true); |
| AddBaseProperty( |
| kDefaultCssImageInlineMaxBytes, |
| &RewriteOptions::css_image_inline_max_bytes_, |
| "cii", kCssImageInlineMaxBytes, |
| kQueryScope, |
| "Number of bytes below which CSS images will be inlined.", true); |
| AddBaseProperty( |
| kDefaultCssInlineMaxBytes, |
| &RewriteOptions::css_inline_max_bytes_, "ci", |
| kCssInlineMaxBytes, |
| kQueryScope, |
| "Number of bytes below which stylesheets will be inlined.", true); |
| AddBaseProperty( |
| kDefaultGoogleFontCssInlineMaxBytes, |
| &RewriteOptions::google_font_css_inline_max_bytes_, "gfci", |
| kGoogleFontCssInlineMaxBytes, |
| kQueryScope, |
| "Number of bytes below which Google Font stylesheets will be inlined.", |
| true); |
| AddBaseProperty( |
| kDefaultCssOutlineMinBytes, |
| &RewriteOptions::css_outline_min_bytes_, "co", |
| kCssOutlineMinBytes, |
| kDirectoryScope, |
| "Number of bytes above which inline CSS resources will be " |
| "outlined.", true); |
| AddBaseProperty( |
| kDefaultImageInlineMaxBytes, |
| &RewriteOptions::image_inline_max_bytes_, "ii", |
| kImageInlineMaxBytes, |
| kQueryScope, |
| "Number of bytes below which images will be inlined.", true); |
| AddBaseProperty( |
| kDefaultJsInlineMaxBytes, |
| &RewriteOptions::js_inline_max_bytes_, "ji", |
| kJsInlineMaxBytes, |
| kQueryScope, |
| "Number of bytes below which javascript will be inlined.", true); |
| AddBaseProperty( |
| kDefaultJsOutlineMinBytes, |
| &RewriteOptions::js_outline_min_bytes_, "jo", |
| kJsOutlineMinBytes, |
| kDirectoryScope, |
| "Number of bytes above which inline Javascript resources will" |
| "be outlined.", true); |
| AddBaseProperty( |
| kDefaultProgressiveJpegMinBytes, |
| &RewriteOptions::progressive_jpeg_min_bytes_, |
| "jp", kProgressiveJpegMinBytes, |
| kDirectoryScope, |
| "Minimum size in bytes for converting a jpeg to progressive", true); |
| AddBaseProperty( |
| kDefaultMaxCacheableResponseContentLength, |
| &RewriteOptions::max_cacheable_response_content_length_, "rcl", |
| kMaxCacheableResponseContentLength, |
| kServerScope, |
| "Maximum length of a cacheable response content.", true); |
| AddBaseProperty( |
| kDefaultMaxHtmlCacheTimeMs, &RewriteOptions::max_html_cache_time_ms_, |
| "hc", kMaxHtmlCacheTimeMs, kDirectoryScope, NULL, |
| true); // TODO(jud): Add doc when split_html is made availabile in MPS. |
| AddBaseProperty( |
| kDefaultMaxHtmlParseBytes, |
| &RewriteOptions::max_html_parse_bytes_, "hpb", |
| kMaxHtmlParseBytes, |
| kDirectoryScope, // TODO(jmarantz): switch to kProcessScope? |
| "Maximum number of bytes of HTML that we parse, before " |
| "redirecting to ?ModPagespeed=off", true); |
| AddBaseProperty( |
| kDefaultMaxImageBytesForWebpInCss, |
| &RewriteOptions::max_image_bytes_for_webp_in_css_, "miwc", |
| kMaxImageBytesForWebpInCss, |
| kDirectoryScope, |
| NULL, true); // TODO(jmarantz): clean this up & doc it, or delete it. |
| // "Maximum byte size of webp images rewritten from CSS" |
| AddBaseProperty( |
| kDefaultMinResourceCacheTimeToRewriteMs, |
| &RewriteOptions::min_resource_cache_time_to_rewrite_ms_, "rc", |
| kMinResourceCacheTimeToRewriteMs, |
| kDirectoryScope, |
| NULL, true); // TODO(jmarantz): remove this or document it. |
| AddBaseProperty( |
| false, |
| &RewriteOptions::oblivious_pagespeed_urls_, "opu", |
| kObliviousPagespeedUrls, |
| kDirectoryScope, |
| NULL, true); // Not applicable for mod_pagespeed. |
| AddBaseProperty( |
| false, |
| &RewriteOptions::rewrite_uncacheable_resources_, "rur", |
| kRewriteUncacheableResources, |
| kServerScope, |
| "Allow optimization of uncacheable resources in the in-place rewriting" |
| " mode.", true); |
| AddBaseProperty( |
| kDefaultIdleFlushTimeMs, |
| &RewriteOptions::idle_flush_time_ms_, "if", |
| kIdleFlushTimeMs, |
| kDirectoryScope, |
| NULL, true); // TODO(jmarantz): implement for mod_pagespeed. |
| AddBaseProperty( |
| kDefaultFlushBufferLimitBytes, |
| &RewriteOptions::flush_buffer_limit_bytes_, "fbl", |
| kFlushBufferLimitBytes, |
| kDirectoryScope, |
| NULL, true); // TODO(jmarantz): implement for mod_pagespeed. |
| AddBaseProperty( |
| kDefaultImplicitCacheTtlMs, |
| &RewriteOptions::implicit_cache_ttl_ms_, "ict", |
| kImplicitCacheTtlMs, |
| kDirectoryScope, |
| "Time in milliseconds to cache resources that lack an Expires or " |
| "Cache-Control header", true); |
| AddBaseProperty( |
| kDefaultLoadFromFileCacheTtlMs, |
| &RewriteOptions::load_from_file_cache_ttl_ms_, "lfct", |
| kLoadFromFileCacheTtlMs, |
| kDirectoryScope, |
| "Time in milliseconds to cache resources loaded from file that lack an " |
| "Expires or Cache-Control header. If not explicitly set, defaults to " |
| "using the value set by implicit_cache_ttl_ms", true); |
| AddBaseProperty( |
| kDefaultImageMaxRewritesAtOnce, |
| &RewriteOptions::image_max_rewrites_at_once_, |
| "im", kImageMaxRewritesAtOnce, |
| kProcessScope, |
| "Set bound on number of images being rewritten at one time " |
| "(0 = unbounded).", true); |
| AddBaseProperty( |
| kDefaultMaxUrlSegmentSize, &RewriteOptions::max_url_segment_size_, |
| "uss", kMaxUrlSegmentSize, |
| kDirectoryScope, |
| "Maximum size of a URL segment.", true); |
| AddBaseProperty( |
| kDefaultMaxUrlSize, &RewriteOptions::max_url_size_, "us", |
| kMaxUrlSize, |
| kDirectoryScope, |
| NULL, true); // TODO(jmarantz): write help & doc for mod_pagespeed. |
| AddBaseProperty( |
| false, &RewriteOptions::forbid_all_disabled_filters_, "fadf", |
| kForbidAllDisabledFilters, |
| kDirectoryScope, |
| "Prevents the use of disabled filters", true); |
| AddBaseProperty( |
| kDefaultRewriteDeadlineMs, &RewriteOptions::rewrite_deadline_ms_, |
| "rdm", kRewriteDeadlineMs, |
| kDirectoryScope, |
| "Time to wait for resource optimization (per flush window) before" |
| "falling back to the original resource for the request.", true); |
| AddBaseProperty( |
| kEnabledOn, &RewriteOptions::enabled_, "e", kEnabled, |
| kDirectoryScope, |
| NULL, true); // initialized explicitly in mod_instaweb.cc. |
| AddBaseProperty( |
| false, &RewriteOptions::add_options_to_urls_, "aou", |
| kAddOptionsToUrls, |
| kDirectoryScope, |
| "Add query-params with configuration adjustments to rewritten " |
| "URLs.", true); |
| |
| // TODO(jmarantz): consider whether to document this option -- it |
| // potentially can hide problems in configuration or bugs. |
| AddBaseProperty( |
| false, &RewriteOptions::publicly_cache_mismatched_hashes_experimental_, |
| "pcmh", |
| kPubliclyCacheMismatchedHashesExperimental, |
| kDirectoryScope, |
| "When serving a request for a .pagespeed. URL with the wrong hash, allow " |
| "public caching based on the origin TTL.", false); |
| |
| AddBaseProperty( |
| true, &RewriteOptions::in_place_rewriting_enabled_, "ipro", |
| kInPlaceResourceOptimization, |
| kDirectoryScope, |
| "Allow rewriting resources even when they are " |
| "fetched over non-pagespeed URLs.", true); |
| AddBaseProperty( |
| false, &RewriteOptions::in_place_wait_for_optimized_, "ipwo", |
| kInPlaceWaitForOptimized, |
| kDirectoryScope, |
| "Wait for optimizations to complete", true); // TODO(jmarantz): Add doc. |
| AddBaseProperty( |
| kDefaultRewriteDeadlineMs, |
| &RewriteOptions::in_place_rewrite_deadline_ms_, "iprdm", |
| kInPlaceRewriteDeadlineMs, |
| kDirectoryScope, |
| "Time to wait for an in-place resource optimization before" |
| "falling back to the original resource for the request.", true); |
| AddBaseProperty( |
| true, &RewriteOptions::in_place_preemptive_rewrite_css_, |
| "ipprc", kInPlacePreemptiveRewriteCss, |
| kDirectoryScope, |
| "If set, issue preemptive rewrites of CSS on the HTML path when " |
| "configured to use IPRO.", true); |
| AddBaseProperty( |
| true, &RewriteOptions::in_place_preemptive_rewrite_css_images_, |
| "ipprci", kInPlacePreemptiveRewriteCssImages, |
| kDirectoryScope, |
| "If set, issue preemptive rewrites of CSS images on the IPRO " |
| "serving path.", true); |
| AddBaseProperty( |
| true, &RewriteOptions::in_place_preemptive_rewrite_images_, |
| "ippri", kInPlacePreemptiveRewriteImages, |
| kDirectoryScope, |
| "If set, issue preemptive rewrites of images on the HTML path " |
| "when configured to use IPRO.", true); |
| AddBaseProperty( |
| true, &RewriteOptions::in_place_preemptive_rewrite_javascript_, |
| "ipprj", kInPlacePreemptiveRewriteJavascript, |
| kDirectoryScope, |
| "If set, issue preemptive rewrites of JS on the HTML path when " |
| "configured to use IPRO.", true); |
| AddBaseProperty( |
| true, &RewriteOptions::private_not_vary_for_ie_, |
| "pnvie", kPrivateNotVaryForIE, |
| kDirectoryScope, |
| "If set, serve in-place optimized resources as Cache-Control: private " |
| "rather than Vary: Accept. Avoids an extra fetch on cache hit, but " |
| "prevents proxy caching of these resources. Only relevant if your " |
| "proxy caches Vary: Accept", true); |
| AddBaseProperty( |
| true, &RewriteOptions::combine_across_paths_, "cp", |
| kCombineAcrossPaths, |
| kDirectoryScope, |
| "Allow combining resources from different paths", true); |
| AddBaseProperty( |
| true, &RewriteOptions::critical_images_beacon_enabled_, "cibe", |
| kCriticalImagesBeaconEnabled, |
| kDirectoryScope, "Enable insertion of client-side critical " |
| "image detection js for image optimization filters.", true); |
| AddBaseProperty( |
| false, &RewriteOptions:: |
| test_only_prioritize_critical_css_dont_apply_original_css_, |
| "dlacae", kTestOnlyPrioritizeCriticalCssDontApplyOriginalCss, |
| kDirectoryScope, |
| "Stops the prioritize_critical_css filter from invoking its JavaScript " |
| "that applies all the 'hidden' CSS at onload. Intended for testing.", |
| false); |
| AddBaseProperty(kDefaultBeaconReinstrumentTimeSec, |
| &RewriteOptions::beacon_reinstrument_time_sec_, "brts", |
| kBeaconReinstrumentTimeSec, kDirectoryScope, |
| "How often (in seconds) to reinstrument pages with beacons. " |
| "This is used for both critical image beaconing, and for the " |
| "prioritize_critical_css filter.", true); |
| AddBaseProperty( |
| false, &RewriteOptions::log_background_rewrites_, "lbr", |
| kLogBackgroundRewrite, |
| kServerScope, |
| NULL, false); // TODO(huibao): write help & doc for mod_pagespeed. |
| AddBaseProperty( |
| false, &RewriteOptions::log_mobilization_samples_, "lms", |
| kLogMobilizationSamples, |
| kDirectoryScope, |
| "Verbose debugging of all sample data" |
| " generated by mobilization_label_filter.", |
| false); |
| AddBaseProperty( |
| false, &RewriteOptions::log_rewrite_timing_, "lr", |
| kLogRewriteTiming, |
| kDirectoryScope, |
| "Whether or not to report timing information about HtmlParse.", false); |
| AddBaseProperty( |
| false, &RewriteOptions::log_url_indices_, "lui", |
| kLogUrlIndices, |
| kDirectoryScope, |
| "Whether or not to log URL indices for rewriter applications.", false); |
| AddBaseProperty( |
| false, &RewriteOptions::lowercase_html_names_, "lh", |
| kLowercaseHtmlNames, |
| kDirectoryScope, |
| "Lowercase tag and attribute names for HTML.", true); |
| AddBaseProperty( |
| false, &RewriteOptions::always_rewrite_css_, "arc", |
| kAlwaysRewriteCss, |
| kDirectoryScope, |
| NULL, true); // TODO(jmarantz): write help & doc for mod_pagespeed. |
| AddBaseProperty( |
| false, &RewriteOptions::respect_vary_, "rv", kRespectVary, |
| kDirectoryScope, |
| "Whether to respect Vary headers for resources. " |
| "Vary is always respected for HTML.", true); |
| AddBaseProperty( |
| false, &RewriteOptions::respect_x_forwarded_proto_, "rxfp", |
| kRespectXForwardedProto, |
| // Note: We mark this as kDirectoryScope because we mistakenly used to. |
| // It does not actually work in directory-scope and is documented to |
| // only work on server-scope. |
| // Note: We must check this option to get the proper URL, but the proper |
| // URL is needed to get directory-specific options, so allowing this in |
| // directory-scope would be a circular dependency. |
| kDirectoryScope, |
| "Whether to respect the X-Forwarded-Proto header.", true); |
| AddBaseProperty( |
| false, &RewriteOptions::flush_html_, "fh", kFlushHtml, |
| kDirectoryScope, |
| NULL, true); // TODO(jmarantz): implement for mod_pagespeed. |
| AddBaseProperty( |
| false, &RewriteOptions::css_preserve_urls_, "cpu", |
| kCssPreserveURLs, |
| kDirectoryScope, |
| "Disable the rewriting of CSS URLs.", true); |
| AddBaseProperty( |
| false, &RewriteOptions::image_preserve_urls_, "ipu", |
| kImagePreserveURLs, |
| kDirectoryScope, |
| "Disable the rewriting of Image URLs.", true); |
| AddBaseProperty( |
| false, &RewriteOptions::js_preserve_urls_, "jpu", |
| kJsPreserveURLs, |
| kDirectoryScope, |
| "Disable the rewriting of Javascript URLs.", true); |
| AddBaseProperty( |
| false, &RewriteOptions::serve_split_html_in_two_chunks_, "sstc", |
| kServeSplitHtmlInTwoChunks, |
| kDirectoryScope, |
| "Serve the split html response in two chunks", true); |
| AddBaseProperty( |
| true, &RewriteOptions::serve_stale_if_fetch_error_, "ss", |
| kServeStaleIfFetchError, |
| kDirectoryScope, |
| NULL, true); // TODO(jmarantz): write help & doc for mod_pagespeed. |
| AddBaseProperty( |
| false, &RewriteOptions::proactively_freshen_user_facing_request_, "pfur", |
| kProactivelyFreshenUserFacingRequest, |
| kDirectoryScope, |
| NULL, true); |
| AddBaseProperty( |
| 0, |
| &RewriteOptions::serve_stale_while_revalidate_threshold_sec_, |
| "sswrt", |
| kServeStaleWhileRevalidateThresholdSec, |
| kDirectoryScope, |
| "Threshold for serving serving stale responses while revalidating in " |
| "background. 0 means don't serve stale content." |
| "Note: Stale response will be served only for non-html requests.", true); |
| AddBaseProperty( |
| false, |
| &RewriteOptions::flush_more_resources_early_if_time_permits_, |
| "fretp", kFlushMoreResourcesEarlyIfTimePermits, |
| kDirectoryScope, |
| NULL, true); // TODO(jmarantz): implement for mod_pagespeed. |
| AddRequestProperty( |
| false, |
| &RewriteOptions::flush_more_resources_in_ie_and_firefox_, |
| "fmrief", true); |
| AddBaseProperty( |
| kDefaultMaxPrefetchJsElements, |
| &RewriteOptions::max_prefetch_js_elements_, "mpje", |
| kMaxPrefetchJsElements, |
| kDirectoryScope, |
| "Set number of JS elements to download without executing. This is useful" |
| "for prefetching script elements when defer JS filter is enabled.", true); |
| AddBaseProperty( |
| false, &RewriteOptions::enable_defer_js_experimental_, "edje", |
| kEnableDeferJsExperimental, |
| kDirectoryScope, |
| "Enable experimental options in defer javascript.", true); |
| AddBaseProperty( |
| false, |
| &RewriteOptions::disable_background_fetches_for_bots_, "dbfb", |
| kDisableBackgroundFetchesForBots, |
| kDirectoryScope, |
| "Disable pre-emptive background fetches on bot requests.", true); |
| AddBaseProperty( |
| true, // By default, don't optimize resource if no-transform is set. |
| &RewriteOptions::disable_rewrite_on_no_transform_, "drnt", |
| kDisableRewriteOnNoTransform, kDirectoryScope, |
| "If false, resource is rewritten even if no-transform header is set", |
| true); |
| AddBaseProperty( |
| false, &RewriteOptions::enable_cache_purge_, "euci", |
| kEnableCachePurge, |
| kServerScope, |
| "Allows individual resources to be flushed; adding some overhead to " |
| "the metadata cache", true); |
| AddBaseProperty( |
| false, &RewriteOptions::proactive_resource_freshening_, "prf", |
| kProactiveResourceFreshening, kServerScope, |
| "If true, allows proactive freshening of inputs to the resource when " |
| "they are close to expiry.", |
| true); // TODO(mpalem): write end user doc in |
| // net/instaweb/doc/en/speed/pagespeed/module/system.html |
| AddBaseProperty( |
| false, &RewriteOptions::lazyload_highres_images_, |
| "elhr", kEnableLazyLoadHighResImages, |
| kDirectoryScope, |
| NULL, true); |
| AddBaseProperty( |
| false, &RewriteOptions::enable_flush_early_critical_css_, "efcc", |
| kEnableFlushEarlyCriticalCss, |
| kDirectoryScope, |
| NULL, true); // Not applicable for mod_pagespeed. |
| AddBaseProperty( |
| false, &RewriteOptions::use_selectors_for_critical_css_, "scss", |
| kUseSelectorsForCriticalCss, |
| kDirectoryScope, |
| NULL, true); // Not applicable for mod_pagespeed. |
| AddBaseProperty( |
| false, &RewriteOptions::default_cache_html_, "dch", |
| kDefaultCacheHtml, |
| kDirectoryScope, |
| NULL, true); // TODO(jmarantz): implement for mod_pagespeed. |
| AddBaseProperty( |
| kDefaultDomainShardCount, &RewriteOptions::domain_shard_count_, |
| "dsc", kDomainShardCount, |
| kQueryScope, |
| NULL, true); // Not applicable for mod_pagespeed. |
| AddBaseProperty( |
| true, &RewriteOptions::modify_caching_headers_, "mch", |
| kModifyCachingHeaders, |
| kDirectoryScope, |
| "Set to false to disallow mod_pagespeed from editing HTML " |
| "Cache-Control headers. This is not safe in general and can cause " |
| "the incorrect versions of HTML to be served to users.", true); |
| |
| // This is not Plain Old Data, so we initialize it here. |
| const RewriteOptions::BeaconUrl kDefaultBeaconUrls = |
| { kDefaultBeaconUrl, kDefaultBeaconUrl, |
| kDefaultBeaconUrl, kDefaultBeaconUrl }; |
| AddBaseProperty( |
| kDefaultBeaconUrls, &RewriteOptions::beacon_url_, "bu", |
| kBeaconUrl, |
| kDirectoryScope, |
| "URL for beacon callback injected by add_instrumentation.", false); |
| |
| // lazyload_images_after_onload_ is especially important for mobile, |
| // where the recommendation is that you prefetch all the |
| // necessary assets (burst your data), and then shutoff the radio to |
| // preserve battery. Further, if the radio has been idle, and then |
| // you scroll, then you'll have to incur the RRC upgrade cost, which |
| // can be anywhere from 100ms-2.5s, which makes the site appear very |
| // slowly.. and even worse if that triggers reflows. |
| // |
| // The problem on mobile is that everytime you wake up the radio, no |
| // matter the size of the transfer, it then has to cycle through |
| // the intermediate power states.. so even a tiny transfers results |
| // in radio consuming power for 10s+. So you incur unnecessary |
| // latency, burn battery, etc. |
| // |
| // http://developer.android.com/training/efficient-downloads/efficient-network-access.html#PrefetchData |
| AddBaseProperty( |
| true, &RewriteOptions::lazyload_images_after_onload_, "llio", |
| kLazyloadImagesAfterOnload, |
| kDirectoryScope, |
| "Wait until page onload before loading lazy images", true); |
| |
| AddBaseProperty( |
| "", &RewriteOptions::request_option_override_, "roo", |
| kRequestOptionOverride, |
| kDirectoryScope, |
| "Token passed in URL to enable pagespeed options in params.", false); |
| AddBaseProperty( |
| "", &RewriteOptions::url_signing_key_, "usk", |
| kUrlSigningKey, |
| kServerScope, |
| "Key used for signing .pagespeed resource URLs.", false); |
| AddBaseProperty( |
| false, &RewriteOptions::accept_invalid_signatures_, "ais", |
| kAcceptInvalidSignatures, kServerScope, |
| "Accept resources with invalid signatures.", false); |
| AddBaseProperty( |
| Timer::kSecondMs, |
| &RewriteOptions::remote_configuration_timeout_ms_, "rcfgt", |
| kRemoteConfigurationTimeoutMs, kServerScope, |
| "Timeout for fetch of remote configuration file.", |
| true); |
| AddBaseProperty( |
| "", |
| &RewriteOptions::remote_configuration_url_, "rcfgu", |
| kRemoteConfigurationUrl, kDirectoryScope, |
| "URL of site from which to pull remote configuration files", |
| true); |
| AddBaseProperty( |
| 9, &RewriteOptions::http_cache_compression_level_, "hccl", |
| kHttpCacheCompressionLevel, kServerScope, |
| "Compression level for HTTPCache. [-1-9] where 0 is off, 1 is minimum" |
| "compression, and 9 (the default) is maximum compression.", |
| true); |
| AddBaseProperty( |
| "", &RewriteOptions::lazyload_images_blank_url_, "llbu", |
| kLazyloadImagesBlankUrl, |
| kDirectoryScope, |
| "URL of image used to display prior to loading the lazy image. " |
| "Empty means use a site-local copy.", true); |
| AddBaseProperty( |
| false, &RewriteOptions::use_blank_image_for_inline_preview_, "biip", |
| kUseBlankImageForInlinePreview, |
| kDirectoryScope, |
| "Use a blank image for inline preview", true); |
| AddBaseProperty( |
| true, &RewriteOptions::inline_only_critical_images_, "ioci", |
| kInlineOnlyCriticalImages, |
| kDirectoryScope, |
| "Inline only critical images", true); |
| AddBaseProperty( |
| ResourceCategorySet(), |
| &RewriteOptions::inline_unauthorized_resource_types_, "irwea", |
| kInlineResourcesWithoutExplicitAuthorization, |
| kDirectoryScope, |
| "Specifies the resource types that can be inlined into HTML even if " |
| "they do not belong to explicitly authorized domains.", true); |
| AddBaseProperty( |
| false, &RewriteOptions::domain_rewrite_cookies_, "drc", |
| kDomainRewriteCookies, |
| kDirectoryScope, |
| "Allow rewrite_domains to rewrite domains in Set-Cookie headers.", true); |
| AddBaseProperty( |
| false, &RewriteOptions::domain_rewrite_hyperlinks_, "drh", |
| kDomainRewriteHyperlinks, |
| kDirectoryScope, |
| "Allow rewrite_domains to rewrite <form> and <a> tags in addition " |
| "to resource tags.", true); |
| AddBaseProperty( |
| false, &RewriteOptions::client_domain_rewrite_, "cdr", |
| kClientDomainRewrite, |
| kDirectoryScope, |
| "Allow rewrite_domains to rewrite urls on the client side.", true); |
| AddBaseProperty( |
| kDefaultImageJpegRecompressQuality, |
| &RewriteOptions::image_jpeg_recompress_quality_, "iq", |
| kImageJpegRecompressionQuality, |
| kQueryScope, |
| "Set quality parameter for recompressing jpeg images [-1,100], " |
| "100 is lossless, -1 uses ImageRecompressionQuality", true); |
| // Use kDefaultImageJpegRecompressQuality as default. |
| AddBaseProperty( |
| kDefaultImageJpegRecompressQualityForSmallScreens, |
| &RewriteOptions::image_jpeg_recompress_quality_for_small_screens_, "iqss", |
| kImageJpegRecompressionQualityForSmallScreens, |
| kQueryScope, |
| "Set quality parameter for recompressing jpeg images for small " |
| "screens. [-1,100], 100 refers to best quality, -1 falls back to " |
| "ImageJpegRecompressionQuality.", true); |
| AddBaseProperty( |
| kDefaultImageJpegQualityForSaveData, |
| &RewriteOptions::image_jpeg_quality_for_save_data_, "iqsd", |
| kImageJpegQualityForSaveData, |
| kQueryScope, |
| "Set quality for the images which will be optimized to JPEG format in " |
| "the Save-Data mode. Use a value in [0,100] to explicitly set the " |
| "quality. Use -1 to ignore the Save-Data header.", true); |
| AddBaseProperty( |
| kDefaultImageRecompressQuality, |
| &RewriteOptions::image_recompress_quality_, "irq", |
| kImageRecompressionQuality, |
| kQueryScope, |
| "Set quality parameter for recompressing images [-1,100], " |
| "100 refers to best quality, -1 disables lossy compression. " |
| "JpegRecompressionQuality and WebpRecompressionQuality override " |
| "this.", true); |
| AddBaseProperty( |
| kDefaultImageLimitOptimizedPercent, |
| &RewriteOptions::image_limit_optimized_percent_, "ip", |
| kImageLimitOptimizedPercent, |
| kDirectoryScope, |
| "Replace images whose size after recompression is less than the " |
| "given percent of original image size; 100 means replace if " |
| "smaller.", true); |
| AddBaseProperty( |
| kDefaultImageLimitRenderedAreaPercent, |
| &RewriteOptions::image_limit_rendered_area_percent_, "ira", |
| kImageLimitRenderedAreaPercent, |
| kDirectoryScope, |
| "Limit on percentage of rendered image wxh to the original " |
| "image wxh that should be stored in the property cache. This is to " |
| "avoid corner cases where rounding off decreases the rendered " |
| "image size by a few pixels.", true); |
| AddBaseProperty( |
| kDefaultImageLimitResizeAreaPercent, |
| &RewriteOptions::image_limit_resize_area_percent_, "ia", |
| kImageLimitResizeAreaPercent, |
| kDirectoryScope, |
| "Consider resizing images whose area in pixels is less than the " |
| "given percent of original image area; 100 means replace if " |
| "smaller.", true); |
| AddBaseProperty( |
| kDefaultImageWebpRecompressQuality, |
| &RewriteOptions::image_webp_recompress_quality_, "iw", |
| kImageWebpRecompressionQuality, |
| kQueryScope, |
| "Quality for rewritten webp images [-1,100], 100 refers to best quality, " |
| "-1 uses ImageRecompressionQuality.", true); |
| // Use kDefaultImageWebpRecompressQuality as default. |
| AddBaseProperty( |
| kDefaultImageWebpRecompressQualityForSmallScreens, |
| &RewriteOptions::image_webp_recompress_quality_for_small_screens_, "iwss", |
| kImageWebpRecompressionQualityForSmallScreens, |
| kQueryScope, |
| "Quality for rewritten webp images for small screens. [-1,100], " |
| "100 refers to best quality, -1 falls back to " |
| "WebpRecompressionQuality.", true); |
| AddBaseProperty( |
| kDefaultImageWebpAnimatedRecompressQuality, |
| &RewriteOptions::image_webp_animated_recompress_quality_, "iwa", |
| kImageWebpAnimatedRecompressionQuality, |
| kQueryScope, |
| "Quality for rewritten animated webp images [-1,100], " |
| "100 refers to best quality, -1 uses ImageRecompressionQuality.", true); |
| AddBaseProperty( |
| kDefaultImageWebpQualityForSaveData, |
| &RewriteOptions::image_webp_quality_for_save_data_, "iwsd", |
| kImageWebpQualityForSaveData, |
| kQueryScope, |
| "Set quality for the images which will be optimized to lossy WebP " |
| "format in the Save-Data mode. Use a value in [0,100] to explicitly set " |
| "the quality. Use -1 to ignore the Save-Data header.", true); |
| AddBaseProperty( |
| kDefaultImageWebpTimeoutMs, |
| &RewriteOptions::image_webp_timeout_ms_, "wt", |
| kImageWebpTimeoutMs, |
| kProcessScope, |
| NULL, true); // TODO(jmarantz): write help & doc for mod_pagespeed. |
| AddBaseProperty( |
| kDefaultMaxInlinedPreviewImagesIndex, |
| &RewriteOptions::max_inlined_preview_images_index_, "mdii", |
| kMaxInlinedPreviewImagesIndex, |
| kDirectoryScope, |
| "Number of first N images for which low resolution image is " |
| "generated. Negative values result in generation for all images.", true); |
| AddBaseProperty( |
| kDefaultMinImageSizeLowResolutionBytes, |
| &RewriteOptions::min_image_size_low_resolution_bytes_, "nislr", |
| kMinImageSizeLowResolutionBytes, |
| kDirectoryScope, |
| "Minimum image size above which low resolution image is " |
| "generated.", true); |
| AddBaseProperty( |
| kDefaultMaxImageSizeLowResolutionBytes, |
| &RewriteOptions::max_image_size_low_resolution_bytes_, "xislr", |
| kMaxImageSizeLowResolutionBytes, |
| kDirectoryScope, |
| "Maximum image size below which low resolution image is " |
| "generated.", true); |
| AddBaseProperty( |
| kDefaultFinderPropertiesCacheExpirationTimeMs, |
| &RewriteOptions::finder_properties_cache_expiration_time_ms_, |
| "fpce", kFinderPropertiesCacheExpirationTimeMs, |
| kDirectoryScope, |
| "Number of ms that beacon results for the critical selector finders " |
| "should be considered valid.", true); |
| AddBaseProperty( |
| kDefaultFinderPropertiesCacheRefreshTimeMs, |
| &RewriteOptions::finder_properties_cache_refresh_time_ms_, |
| "fpcr", kFinderPropertiesCacheRefreshTimeMs, |
| kDirectoryScope, |
| NULL, true); // Not applicable for mod_pagespeed. |
| AddBaseProperty( |
| kDefaultExperimentCookieDurationMs, |
| &RewriteOptions::experiment_cookie_duration_ms_, "fcd", |
| kExperimentCookieDurationMs, |
| kDirectoryScope, |
| NULL, true); // TODO(jmarantz): write help & doc for mod_pagespeed. |
| AddBaseProperty( |
| kDefaultImageJpegNumProgressiveScans, |
| &RewriteOptions::image_jpeg_num_progressive_scans_, "ijps", |
| kImageJpegNumProgressiveScans, |
| kDirectoryScope, |
| "Number of progressive scans [1,10] to emit when rewriting images as " |
| "ten-scan progressive jpegs. " |
| "A value of -1 outputs all progressive scans.", true); |
| // Use kDefaultImageJpegNumProgressiveScans as default. |
| AddBaseProperty( |
| kDefaultImageJpegNumProgressiveScans, |
| &RewriteOptions::image_jpeg_num_progressive_scans_for_small_screens_, |
| "ijpst", |
| kImageJpegNumProgressiveScansForSmallScreens, |
| kDirectoryScope, |
| "Number of progressive scans [1,10] to emit when rewriting images as" |
| "ten-scan progressive jpegs for small screens. A value of -1 falls " |
| "back to kImageJpegNumProgressiveScans.", true); |
| AddBaseProperty( |
| false, &RewriteOptions::cache_small_images_unrewritten_, "csiu", |
| kCacheSmallImagesUnrewritten, |
| kDirectoryScope, |
| NULL, true); // TODO(jmarantz): write help & doc for mod_pagespeed. |
| AddBaseProperty( |
| kDefaultImageResolutionLimitBytes, |
| &RewriteOptions::image_resolution_limit_bytes_, |
| "irlb", kImageResolutionLimitBytes, |
| kDirectoryScope, |
| "Maximum byte size of an image for optimization", true); |
| AddBaseProperty( |
| 0, &RewriteOptions::rewrite_random_drop_percentage_, "rrdp", |
| kRewriteRandomDropPercentage, kDirectoryScope, |
| "The percentage of time that pagespeed should randomly drop an " |
| "opportunity to optimize an image. The value should be an integer " |
| "between 0 and 100 inclusive.", true); |
| AddBaseProperty( |
| "", &RewriteOptions::ga_id_, "ig", kAnalyticsID, |
| kDirectoryScope, |
| "Google Analytics ID to use on site.", true); |
| AddBaseProperty( |
| "", &RewriteOptions::content_experiment_id_, "cxid", kContentExperimentID, |
| kDirectoryScope, |
| "Which Google Analytics content experiment to log to.", true); |
| AddBaseProperty( |
| "", &RewriteOptions::content_experiment_variant_id_, "cxvid", |
| kContentExperimentVariantID, |
| kDirectoryScope, |
| "Which Google Analytics content experiment variant to log to.", true); |
| AddBaseProperty( |
| true, &RewriteOptions::use_analytics_js_, "uajs", |
| kUseAnalyticsJs, |
| kQueryScope, |
| "Log to analytics.js instead of ga.js with insert_ga.", true); |
| AddBaseProperty( |
| true, &RewriteOptions::increase_speed_tracking_, "st", |
| kIncreaseSpeedTracking, |
| kDirectoryScope, |
| NULL, true); // TODO(jmarantz): write help & doc for mod_pagespeed. |
| AddBaseProperty( |
| false, &RewriteOptions::running_experiment_, "fur", kRunningExperiment, |
| kDirectoryScope, |
| NULL, true); // Not applicable for mod_pagespeed. |
| AddBaseProperty( |
| kDefaultExperimentSlot, &RewriteOptions::experiment_ga_slot_, "fga", |
| kExperimentSlot, |
| kDirectoryScope, |
| NULL, true); // Not applicable for mod_pagespeed. |
| AddBaseProperty( |
| experiment::kForceNoExperiment, &RewriteOptions::enroll_experiment_id_, |
| "eeid", |
| kEnrollExperiment, |
| kQueryScope, |
| "Assign users to a specific experiment setting.", true); |
| AddBaseProperty( |
| false, &RewriteOptions::report_unload_time_, "rut", |
| kReportUnloadTime, |
| kDirectoryScope, |
| "If set reports optional page unload time.", true); |
| AddBaseProperty( |
| "", &RewriteOptions::x_header_value_, "xhv", |
| kXModPagespeedHeaderValue, |
| kDirectoryScope, |
| "Set the value for the X-Mod-Pagespeed HTTP header", true); |
| AddBaseProperty(true, &RewriteOptions::distribute_fetches_, "dfe", |
| kDistributeFetches, kProcessScope, |
| "Whether or not to distribute IPRO and .pagespeed. resource " |
| "fetch requests from the RewriteDriver before checking the " |
| "cache.", true); |
| AddBaseProperty( |
| "", &RewriteOptions::distributed_rewrite_key_, "drwk", |
| kDistributedRewriteKey, kProcessScope, |
| "The key used to authenticate requests from one rewrite task " |
| "to another. This should be random, greater than 8 characters (longer " |
| "is better), and the same value on each mod_pagespeed server config in " |
| "the rewrite cluster.", false); |
| AddBaseProperty( |
| "", &RewriteOptions::distributed_rewrite_servers_, "drws", |
| kDistributedRewriteServers, kProcessScope, |
| "A comma-separated list of hosts to use for distributed rewrites.", false); |
| AddBaseProperty( |
| kDefaultDistributedTimeoutMs, |
| &RewriteOptions::distributed_rewrite_timeout_ms_, "drwt", |
| kDistributedRewriteTimeoutMs, kProcessScope, |
| "Time to wait before giving up on a distributed rewrite request.", false); |
| AddBaseProperty( |
| true, &RewriteOptions::avoid_renaming_introspective_javascript_, |
| "aris", kAvoidRenamingIntrospectiveJavascript, |
| kDirectoryScope, |
| "Don't combine, inline, cache extend, or otherwise modify " |
| "javascript in ways that require changing the URL if we see " |
| "introspection in the form of " |
| "document.getElementsByTagName('script').", true); |
| AddBaseProperty( |
| false, &RewriteOptions::reject_blacklisted_, "rbl", |
| kRejectBlacklisted, |
| kDirectoryScope, |
| NULL, false); // Not applicable for mod_pagespeed. |
| AddBaseProperty( |
| HttpStatus::kForbidden, |
| &RewriteOptions::reject_blacklisted_status_code_, "rbls", |
| kRejectBlacklistedStatusCode, |
| kDirectoryScope, |
| NULL, false); // Not applicable for mod_pagespeed. |
| AddBaseProperty( |
| kDefaultBlockingRewriteKey, &RewriteOptions::blocking_rewrite_key_, |
| "blrw", kXPsaBlockingRewrite, |
| kServerScope, |
| "If the X-PSA-Pagespeed-Blocking-Rewrite header is present, and " |
| "its value matches the configured value, ensure that all " |
| "rewrites are completed before sending the response to the " |
| "client.", false); |
| AddBaseProperty( |
| false, |
| &RewriteOptions::use_fallback_property_cache_values_, |
| "fbcv", kUseFallbackPropertyCacheValues, |
| kServerScope, |
| "If this is set to true, fallback values will be used from property " |
| "cache if actual value is not present. Here fallback values means " |
| "properties which are shared across all requests which have same url " |
| "if query paramaters are removed. Example: http://www.test.com?a=1 and " |
| "http://www.test.com?a=2 share same fallback properties though they " |
| "are two different urls.", true); |
| AddBaseProperty( |
| false, |
| &RewriteOptions::await_pcache_lookup_, |
| "wpcl", kAwaitPcacheLookup, |
| kServerScope, |
| NULL, true); |
| AddBaseProperty( |
| true, &RewriteOptions::support_noscript_enabled_, "snse", |
| kSupportNoScriptEnabled, |
| kDirectoryScope, |
| "Support for clients with no script support, in filters that " |
| "insert new javascript.", true); |
| AddBaseProperty( |
| false, &RewriteOptions::enable_extended_instrumentation_, "eei", |
| kEnableExtendedInstrumentation, |
| kDirectoryScope, |
| "If set to true, addition instrumentation js is added to that page that " |
| "the beacon can collect more information.", true); |
| AddBaseProperty( |
| true, &RewriteOptions::use_experimental_js_minifier_, "uejsm", |
| kUseExperimentalJsMinifier, |
| kDirectoryScope, |
| "If set to false, uses the old legacy::MinifyJs-based minifier. " |
| "This option will be deprecated once we do a successful release with the " |
| "new minifier.", true); |
| AddBaseProperty( |
| kDefaultMaxCombinedCssBytes, |
| &RewriteOptions::max_combined_css_bytes_, "xcc", |
| kMaxCombinedCssBytes, |
| kQueryScope, |
| "Maximum size allowed for the combined CSS resource.", true); |
| AddBaseProperty( |
| kDefaultMaxCombinedJsBytes, |
| &RewriteOptions::max_combined_js_bytes_, "xcj", |
| kMaxCombinedJsBytes, |
| kDirectoryScope, |
| "Maximum size allowed for the combined JavaScript resource.", true); |
| AddBaseProperty( |
| false, &RewriteOptions::enable_blink_html_change_detection_, |
| "ebhcd", kEnableBlinkHtmlChangeDetection, |
| kDirectoryScope, |
| NULL, false); // Not applicable for mod_pagespeed. |
| // Currently not applicable for mod_pagespeed. |
| AddBaseProperty( |
| false, |
| &RewriteOptions::enable_blink_html_change_detection_logging_, |
| "ebhcdl", kEnableBlinkHtmlChangeDetectionLogging, |
| kDirectoryScope, |
| NULL, false); // Not applicable for mod_pagespeed. |
| AddBaseProperty( |
| "", &RewriteOptions::critical_line_config_, "clc", |
| kCriticalLineConfig, |
| kDirectoryScope, |
| "Critical line xpath config for use by the split html filter.", true); |
| AddBaseProperty( |
| -1, &RewriteOptions::override_caching_ttl_ms_, "octm", |
| kOverrideCachingTtlMs, |
| kDirectoryScope, |
| NULL, true); // TODO(jmarantz): write help & doc for mod_pagespeed. |
| AddBaseProperty( |
| kDefaultMinCacheTtlMs, |
| &RewriteOptions::min_cache_ttl_ms_, "mctm", |
| kMinCacheTtlMs, |
| kDirectoryScope, |
| NULL, true); |
| AddBaseProperty( |
| 5 * Timer::kSecondMs, &RewriteOptions::blocking_fetch_timeout_ms_, |
| "bfto", RewriteOptions::kFetcherTimeOutMs, |
| kDirectoryScope, |
| NULL, true); // TODO(jmarantz): write help & doc for mod_pagespeed. |
| AddBaseProperty( |
| false, &RewriteOptions::enable_prioritizing_scripts_, "eps", |
| kEnablePrioritizingScripts, |
| kDirectoryScope, |
| NULL, true); // Not applicable for mod_pagespeed. |
| AddRequestProperty( |
| "", &RewriteOptions::pre_connect_url_, "pcu", true); |
| AddRequestProperty( |
| kDefaultPropertyCacheHttpStatusStabilityThreshold, |
| &RewriteOptions::property_cache_http_status_stability_threshold_, |
| "pchsst", false); |
| AddBaseProperty( |
| kDefaultMaxRewriteInfoLogSize, |
| &RewriteOptions::max_rewrite_info_log_size_, "mrils", |
| kMaxRewriteInfoLogSize, |
| kDirectoryScope, |
| NULL, false); // Not applicable for mod_pagespeed. |
| AddBaseProperty( |
| kDefaultMetadataCacheStalenessThresholdMs, |
| &RewriteOptions::metadata_cache_staleness_threshold_ms_, "mcst", |
| kMetadataCacheStalenessThresholdMs, |
| kDirectoryScope, |
| NULL, true); // TODO(jmarantz): write help & doc for mod_pagespeed. |
| AddBaseProperty( |
| kDefaultDownstreamCachePurgeMethod, |
| &RewriteOptions::downstream_cache_purge_method_, "dcpm", |
| kDownstreamCachePurgeMethod, kDirectoryScope, |
| "Method to be used for purging responses from the downstream cache", |
| false); |
| AddBaseProperty( |
| "", &RewriteOptions::downstream_cache_rebeaconing_key_, "dcrk", |
| kDownstreamCacheRebeaconingKey, kDirectoryScope, |
| "The key used to authenticate rebeaconing requests from downstream " |
| "caches. The value specified for this key in the pagespeed server " |
| "config should be used in the caching layer configuration also.", false); |
| AddBaseProperty( |
| kDefaultDownstreamCacheRewrittenPercentageThreshold, |
| &RewriteOptions::downstream_cache_rewritten_percentage_threshold_, |
| "dcrpt", |
| kDownstreamCacheRewrittenPercentageThreshold, |
| kDirectoryScope, |
| "Threshold for percentage of rewriting to be finished before the " |
| "response is served out and simultaneously stored in the downstream " |
| "cache, beyond which the response will not be purged from the cache even" |
| "if more rewriting is possible now", true); |
| AddRequestProperty( |
| kDefaultMetadataInputErrorsCacheTtlMs, |
| &RewriteOptions::metadata_input_errors_cache_ttl_ms_, |
| "mect", true); |
| AddRequestProperty( |
| true, &RewriteOptions::enable_blink_debug_dashboard_, "ebdd", false); |
| AddRequestProperty( |
| kDefaultBlinkHtmlChangeDetectionTimeMs, |
| &RewriteOptions::blink_html_change_detection_time_ms_, |
| "bhcdt", false); |
| AddRequestProperty( |
| false, &RewriteOptions::override_ie_document_mode_, |
| "oidm", true); |
| AddBaseProperty( |
| false, &RewriteOptions::use_smart_diff_in_blink_, "usdb", |
| kUseSmartDiffInBlink, |
| kDirectoryScope, |
| NULL, false); // Not applicable for mod_pagespeed. |
| |
| // Note: defer_javascript and defer_iframe were previously not |
| // trusted on mobile user-agents, but have now matured to the point |
| // where we should trust them by default. The mod_pagespeed |
| // config-file setting "ModPagespeedEnableAggressiveRewritersForMobile" |
| // will work, but we will omit it from the documentation because we |
| // are enabling it by default. |
| AddBaseProperty( |
| true, &RewriteOptions::enable_aggressive_rewriters_for_mobile_, |
| "earm", kEnableAggressiveRewritersForMobile, |
| kDirectoryScope, |
| "Allows defer_javascript and defer_iframe for mobile browsers", true); |
| |
| AddBaseProperty( |
| false, &RewriteOptions::serve_ghost_click_buster_with_split_html_, |
| "sgcbsh", kServeGhostClickBusterWithSplitHtml, kDirectoryScope, |
| "Serve ghost click buster code along with split html", false); |
| |
| AddBaseProperty(false, &RewriteOptions::serve_xhr_access_control_headers_, |
| "shach", kServeXhrAccessControlHeaders, kDirectoryScope, |
| "Serve access control headers with response headers", false); |
| |
| AddBaseProperty( |
| "", &RewriteOptions::access_control_allow_origins_, |
| "acao", kAccessControlAllowOrigins, |
| kDirectoryScope, |
| "Comma seperated list of origins that are allowed to make cross-origin " |
| "requests", false); |
| |
| AddBaseProperty( |
| false, &RewriteOptions::hide_referer_using_meta_, |
| "hrum", kHideRefererUsingMeta, |
| kDirectoryScope, |
| "Hides the referer by adding meta tag to the HTML", true); |
| |
| AddRequestProperty( |
| -1, &RewriteOptions::blink_blacklist_end_timestamp_ms_, "bbet", false); |
| |
| AddBaseProperty( |
| false, &RewriteOptions::preserve_subresource_hints_, "psrh", |
| kPreserveSubresourceHints, kQueryScope, |
| "Keep original subresource hints in place.", |
| true); |
| |
| AddBaseProperty( |
| true, &RewriteOptions::preserve_url_relativity_, "pur", |
| kPreserveUrlRelativity, kDirectoryScope, |
| "Keep rewritten URLs as relative as the original resource URL was.", |
| true); |
| |
| AddBaseProperty( |
| false, &RewriteOptions::allow_logging_urls_in_log_record_, |
| "alulr", kAllowLoggingUrlsInLogRecord, kDirectoryScope, |
| NULL, false); // Not applicable for mod_pagespeed. |
| |
| AddBaseProperty( |
| true, &RewriteOptions::allow_options_to_be_set_by_cookies_, |
| "aotbsbc", kAllowOptionsToBeSetByCookies, kDirectoryScope, |
| "Allow options to be set by cookies in addition to query parameters " |
| "and request headers.", true); |
| |
| AddBaseProperty( |
| "", &RewriteOptions::non_cacheables_for_cache_partial_html_, "nccp", |
| kNonCacheablesForCachePartialHtml, |
| kDirectoryScope, |
| NULL, false); // Not applicable for mod_pagespeed. |
| |
| AddBaseProperty( |
| false, &RewriteOptions::no_transform_optimized_images_, "ntoi", |
| kNoTransformOptimizedImages, |
| kDirectoryScope, |
| "Add no-transform header to cache-control for optimized images", true); |
| |
| AddBaseProperty( |
| kDefaultMaxLowResImageSizeBytes, |
| &RewriteOptions::max_low_res_image_size_bytes_, |
| "lris", |
| kMaxLowResImageSizeBytes, |
| kDirectoryScope, |
| NULL, true); // TODO(bharathbhushan): write help & doc for mod_pagespeed. |
| |
| AddBaseProperty( |
| kDefaultMaxLowResToFullResImageSizePercentage, |
| &RewriteOptions::max_low_res_to_full_res_image_size_percentage_, |
| "lrhrs", |
| kMaxLowResToHighResImageSizePercentage, |
| kDirectoryScope, |
| NULL, true); // TODO(bharathbhushan): write help & doc for mod_pagespeed. |
| |
| AddBaseProperty( |
| true, |
| &RewriteOptions::serve_rewritten_webp_urls_to_any_agent_, |
| "swaa", |
| kServeWebpToAnyAgent, |
| kDirectoryScope, |
| "Serve rewritten .webp images to any user-agent", true); |
| |
| AddBaseProperty( |
| "", &RewriteOptions::cache_fragment_, "ckp", kCacheFragment, |
| kDirectoryScope, |
| "Set a cache fragment to allow servers with different hostnames to " |
| "share a cache. Allowed: letters, numbers, underscores, and hyphens.", |
| false); |
| |
| AddBaseProperty( |
| "", |
| &RewriteOptions::sticky_query_parameters_, |
| "sqp", |
| kStickyQueryParameters, |
| kDirectoryScope, |
| "The token that must be set by the PageSpeedStickyQueryParameters query " |
| "parameter/header in a request to enable the setting of cookies for all " |
| "other PageSpeed query parameters/headers in the request. Blank means " |
| "it is disabled.", false); |
| AddBaseProperty( |
| kDefaultOptionCookiesDurationMs, |
| &RewriteOptions::option_cookies_duration_ms_, |
| "ocd", |
| kOptionCookiesDurationMs, |
| kDirectoryScope, |
| "The max-age in ms of cookies that set PageSpeed options.", true); |
| |
| ResponsiveDensities default_densities; |
| default_densities.assign( |
| kDefaultResponsiveImageDensities, kDefaultResponsiveImageDensities + |
| arraysize(kDefaultResponsiveImageDensities)); |
| AddBaseProperty( |
| default_densities, &RewriteOptions::responsive_image_densities_, |
| "rid", kResponsiveImageDensities, kDirectoryScope, |
| "Comma separated list of screen densities to target with " |
| "ResponsiveImageFilter srcsets.", true); |
| |
| AllowVaryOn default_allow_vary_on; |
| ParseFromString(AllowVaryOn::kAutoString, &default_allow_vary_on); |
| AddBaseProperty( |
| default_allow_vary_on, &RewriteOptions::allow_vary_on_, |
| "avo", kAllowVaryOn, kQueryScope, |
| "\"Auto\", \"None\", or comma separated list of strings chosen from " |
| "\"Save-Data\", \"User-Agent\", and \"Accept\".", true); |
| |
| AddBaseProperty( |
| false, &RewriteOptions::mob_always_, "malways", kAlwaysMobilize, |
| kQueryScope, |
| "(experimental) Unconditionally mobilize page regardless of user-agent.", |
| true); |
| AddBaseProperty( |
| "", &RewriteOptions::mob_beacon_category_, "mbeaconcat", |
| kMobBeaconCategory, kDirectoryScope, |
| "(experimental) Additional category info to pass to beacon.", |
| true); |
| AddBaseProperty( |
| kDefaultBeaconUrl, &RewriteOptions::mob_beacon_url_, "mbeaconurl", |
| kMobBeaconUrl, kDirectoryScope, |
| "(experimental) URL for beacons sent from the mobilization filter.", |
| true); |
| AddBaseProperty( |
| "", &RewriteOptions::mob_map_location_, "mlocation", kMobMapLocation, |
| kQueryScope, |
| "(experimental) map location for click-to-navigate. This should be in " |
| "a form suitable for sending to a map query, and will not be exposed in " |
| "a web UI directly until map button is pressed", true); |
| AddBaseProperty( |
| "", &RewriteOptions::mob_phone_number_, "mphone", kMobPhoneNumber, |
| kQueryScope, |
| "(experimental) phone number for click-to-call. This should be in " |
| "a form suitable for sending to a dialer, and will not be exposed in " |
| "a web UI directly until call button is pressed", true); |
| AddBaseProperty( |
| 0, &RewriteOptions::mob_conversion_id_, "mcnvid", kMobConversionId, |
| kQueryScope, |
| "(experimental) conversion ID", true); |
| AddBaseProperty( |
| "", &RewriteOptions::mob_map_conversion_label_, "mpcnvl", |
| kMobMapConversionLabel, kQueryScope, |
| "(experimental) phone conversion Label", true); |
| AddBaseProperty( |
| "", &RewriteOptions::mob_phone_conversion_label_, "mmcnvl", |
| kMobPhoneConversionLabel, kQueryScope, |
| "(experimental) map conversion Label", true); |
| AddBaseProperty( |
| false, &RewriteOptions::mob_config_, "mconfig", kMobConfig, |
| kQueryScope, |
| "(experimental) whether to load interactive configuration GUI", true); |
| AddBaseProperty( |
| false, &RewriteOptions::mob_iframe_, "miframe", kMobIframe, |
| kQueryScope, |
| "(experimental) whether to use an iframe rather than proxying", true); |
| AddBaseProperty( |
| false, &RewriteOptions::mob_iframe_disable_, "miframedis", |
| kMobIframeDisable, kDirectoryScope, |
| "(experimental) serves a redirect to the original page for every request " |
| "for which MobIFrame is enabled. Intended as a safety valve when an " |
| "iframed site is sending us traffic and we don't want to actually " |
| "mobilize it.", true); |
| // Note that setting this option to "none" turns off inserting an iframe. We |
| // use this because it's otherwise difficult to set the option to a blank |
| // string to override the default. |
| AddBaseProperty( |
| "width=device-width,initial-scale=1", |
| &RewriteOptions::mob_iframe_viewport_, "miframev", kMobIframeViewport, |
| kQueryScope, |
| "(experimental) the content of the viewport tag to insert " |
| "when in iframe mode. Set to \"none\" to avoid adding a viewport tag.", |
| true); |
| AddBaseProperty( |
| false, &RewriteOptions::mob_layout_, "mlayout", kMobLayout, |
| kQueryScope, |
| "(experimental) whether to run layout resynthesis when mobilizing", true); |
| AddBaseProperty( |
| false, &RewriteOptions::mob_nav_, "mnav", kMobNav, |
| kQueryScope, |
| "(experimental) whether to run navigation resynthesis when mobilizing", |
| true); |
| // TODO(jud): Rename or remove this option once we've settled on a final |
| // design for the mobilization header bar. |
| AddBaseProperty( |
| false, &RewriteOptions::mob_labeled_mode_, "mlabeled", kMobLabeledMode, |
| kQueryScope, |
| "(experimental) whether to use text labels in the header bar. Also " |
| "disables the menu button and the logo.", |
| true); |
| AddBaseProperty( |
| "", &RewriteOptions::mob_nav_classes_, "navcls", kMobNavClasses, |
| kQueryScope, |
| "Comma separated list of element classes or ids to treat as " |
| "navigational, or with leading - as non-navigational", true); |
| AddBaseProperty( |
| false, &RewriteOptions::mob_static_, "mstatic", kMobStatic, |
| kQueryScope, |
| "(experimental) whether to load discrete mobilization JS", |
| true); |
| AddBaseProperty( |
| MobTheme(), &RewriteOptions::mob_theme_, "mtheme", kMobTheme, |
| kServerScope, // DO NOT MAKE THIS QueryScope --- that would XSS. |
| "(experimental) pre-computed theme for mobilization JS", |
| true); |
| |
| // Test-only, so no enum. |
| AddRequestProperty(false, |
| &RewriteOptions::test_instant_fetch_rewrite_deadline_, |
| "tifrwd", false); |
| // We need to exclude this test-only option from signature, since we may need |
| // to change it in the middle of tests. |
| properties_->property(properties_->size() - 1) |
| ->set_do_not_use_for_signature_computation(true); |
| |
| AddBaseProperty( |
| 0, &RewriteOptions::noop_, "noop", kNoop, |
| kQueryScope, |
| "Meaningless integer option for browser cache-busting in query-params", |
| true); |
| properties_->property(properties_->size() - 1) |
| ->set_do_not_use_for_signature_computation(true); |
| |
| // |
| // Recently sriharis@ excluded a variety of options from |
| // signature-computation which makes sense from the perspective |
| // of metadata cache, however it makes Signature() useless for |
| // determining equivalence of RewriteOptions. This equivalence |
| // is needed in ServerContext::NewRewriteDriver to determine |
| // whether the drivers in the freelist are still applicable, or |
| // whether options have changed. |
| // |
| // So we need to either compute two signatures: one for equivalence |
| // and one for metadata cache key, or just use the more comprehensive |
| // one for metadata_cache. We should determine whether we are getting |
| // spurious cache fragmentation before investing in computing two |
| // signatures. |
| // |
| // Commenting these out for now. |
| // |
| // In particular, ProxyInterfaceTest.AjaxRewritingForCss will fail |
| // if we don't let in_place_rewriting_enabled_ affect the signature. |
| // |
| // TODO(jmarantz): consider whether there's any measurable benefit |
| // from excluding these options from the signature. If there is, |
| // make 2 signatures: one for equivalence & one for metadata cache |
| // keys. If not, just remove the DoNotUseForSignatureComputation |
| // infrastructure. |
| // |
| // in_place_rewriting_enabled_.DoNotUseForSignatureComputation(); |
| // log_background_rewrites_.DoNotUseForSignatureComputation(); |
| // log_rewrite_timing_.DoNotUseForSignatureComputation(); |
| // log_url_indices_.DoNotUseForSignatureComputation(); |
| // serve_stale_if_fetch_error_.DoNotUseForSignatureComputation(); |
| // enable_defer_js_experimental_.DoNotUseForSignatureComputation(); |
| // default_cache_html_.DoNotUseForSignatureComputation(); |
| // lazyload_images_after_onload_.DoNotUseForSignatureComputation(); |
| // ga_id_.DoNotUseForSignatureComputation(); |
| // increase_speed_tracking_.DoNotUseForSignatureComputation(); |
| // running_experiment_.DoNotUseForSignatureComputation(); |
| // x_header_value_.DoNotUseForSignatureComputation(); |
| // blocking_fetch_timeout_ms_.DoNotUseForSignatureComputation(); |
| } // NOLINT (large function) |
| |
| RewriteOptions::~RewriteOptions() { |
| STLDeleteElements(&custom_fetch_headers_); |
| STLDeleteElements(&experiment_specs_); |
| STLDeleteElements(&url_cache_invalidation_entries_); |
| STLDeleteValues(&rejected_request_map_); |
| } // NOLINT |
| |
| void RewriteOptions::InitializeOptions(const Properties* properties) { |
| all_options_.resize(all_properties_->size()); |
| |
| // Note that we reserve space in all_options_ for all RewriteOptions |
| // and subclass properties, but we initialize only the options |
| // corresponding to the ones passed into this method, whether from |
| // RewriteOptions or a subclass. |
| // |
| // This is because the member variables for the subclass properties |
| // have not been constructed yet, so copying default values into |
| // them would crash (at least the strings). So we rely on subclass |
| // constructors to initialize their own options by calling |
| // InitializeOptions on their own property sets as well. |
| for (int i = 0, n = properties->size(); i < n; ++i) { |
| const PropertyBase* property = properties->property(i); |
| property->InitializeOption(this); |
| } |
| initialized_options_ += properties->size(); |
| } |
| |
| RewriteOptions::OptionBase::~OptionBase() { |
| } |
| |
| RewriteOptions::Properties::Properties() |
| : initialization_count_(1), |
| owns_properties_(true) { |
| } |
| |
| RewriteOptions::Properties::~Properties() { |
| if (owns_properties_) { |
| STLDeleteElements(&property_vector_); |
| } |
| } |
| |
| RewriteOptions::PropertyBase::~PropertyBase() { |
| } |
| |
| bool RewriteOptions::Properties::Initialize(Properties** properties_handle) { |
| Properties* properties = *properties_handle; |
| if (properties == NULL) { |
| *properties_handle = new Properties; |
| return true; |
| } |
| ++(properties->initialization_count_); |
| return false; |
| } |
| |
| void RewriteOptions::Properties::Merge(Properties* properties) { |
| // We merge all subclass properties up into RewriteOptions::all_properties_. |
| // RewriteOptions::properties_.owns_properties_ is true. |
| // RewriteOptions::all_properties_.owns_properties_ is false. |
| DCHECK(properties->owns_properties_); |
| owns_properties_ = false; |
| property_vector_.reserve(size() + properties->size()); |
| property_vector_.insert(property_vector_.end(), |
| properties->property_vector_.begin(), |
| properties->property_vector_.end()); |
| std::sort(property_vector_.begin(), property_vector_.end(), |
| RewriteOptions::PropertyLessThanByOptionName); |
| for (int i = 0, n = property_vector_.size(); i < n; ++i) { |
| property_vector_[i]->set_index(i); |
| } |
| } |
| |
| bool RewriteOptions::Properties::Terminate(Properties** properties_handle) { |
| Properties* properties = *properties_handle; |
| DCHECK_GT(properties->initialization_count_, 0); |
| if (--(properties->initialization_count_) == 0) { |
| delete properties; |
| *properties_handle = NULL; |
| return true; |
| } |
| return false; |
| } |
| |
| bool RewriteOptions::Initialize() { |
| if (Properties::Initialize(&properties_)) { |
| Properties::Initialize(&all_properties_); |
| AddProperties(); |
| InitFilterIdToEnumArray(); |
| all_properties_->Merge(properties_); |
| InitOptionIdToPropertyArray(); |
| InitOptionNameToPropertyArray(); |
| |
| for (int f = 0; f < static_cast<int>(RewriteOptions::kEndOfFilters); ++f) { |
| RewriteOptions::Filter filter = static_cast<RewriteOptions::Filter>(f); |
| FilterProperties* property = &filter_properties[f]; |
| property->level_core = |
| IsInSet(kCoreFilterSet, arraysize(kCoreFilterSet), filter); |
| property->level_optimize_for_bandwidth = |
| IsInSet(kOptimizeForBandwidthFilterSet, |
| arraysize(kOptimizeForBandwidthFilterSet), filter); |
| property->level_mobilize = |
| IsInSet(kMobilizeFilterSet, arraysize(kMobilizeFilterSet), filter); |
| property->level_test = |
| IsInSet(kTestFilterSet, arraysize(kTestFilterSet), filter); |
| property->level_dangerous = |
| IsInSet(kDangerousFilterSet, arraysize(kDangerousFilterSet), filter); |
| property->preserve_js_urls = |
| IsInSet(kJsPreserveUrlDisabledFilters, |
| arraysize(kJsPreserveUrlDisabledFilters), filter); |
| property->preserve_css_urls = |
| IsInSet(kCssPreserveUrlDisabledFilters, |
| arraysize(kCssPreserveUrlDisabledFilters), filter); |
| property->preserve_image_urls = |
| IsInSet(kImagePreserveUrlDisabledFilters, |
| arraysize(kImagePreserveUrlDisabledFilters), filter); |
| } |
| |
| return true; |
| } |
| return false; |
| } |
| |
| void RewriteOptions::InitFilterIdToEnumArray() { |
| // Sanity-checks -- will be active only when compiled for debug. |
| #ifndef NDEBUG |
| // The forward map must have an entry for every Filter enum value except |
| // the sentinel (kEndOfFilters) and they must be in order. |
| DCHECK_EQ(arraysize(kFilterVectorStaticInitializer), |
| static_cast<size_t>(kEndOfFilters)); |
| for (int i = 0, n = arraysize(kFilterVectorStaticInitializer); i < n; ++i) { |
| DCHECK_EQ(i, |
| static_cast<int>(kFilterVectorStaticInitializer[i].filter_enum)); |
| } |
| // The reverse map must have the same number of elements as the forward map. |
| DCHECK_EQ(arraysize(kFilterVectorStaticInitializer), |
| arraysize(filter_id_to_enum_array_)); |
| #endif |
| // Initialize the reverse map. |
| for (int i = 0, n = arraysize(kFilterVectorStaticInitializer); i < n; ++i) { |
| filter_id_to_enum_array_[i] = &kFilterVectorStaticInitializer[i]; |
| } |
| std::sort(filter_id_to_enum_array_, |
| filter_id_to_enum_array_ + arraysize(filter_id_to_enum_array_), |
| RewriteOptions::FilterEnumToIdAndNameEntryLessThanById); |
| } |
| |
| struct RewriteOptions::OptionIdCompare { |
| bool operator()(const PropertyBase* a, StringPiece b) const { |
| return StringCaseCompare(a->id(), b) < 0; |
| } |
| bool operator()(StringPiece a, const PropertyBase* b) const { |
| return StringCaseCompare(a, b->id()) < 0; |
| } |
| bool operator()(const PropertyBase* a, const PropertyBase* b) const { |
| return StringCaseCompare(a->id(), b->id()) < 0; |
| } |
| }; |
| |
| void RewriteOptions::InitOptionIdToPropertyArray() { |
| // This method is called first by Initialize, when base properties are |
| // added, then zero or more times when subclass properties are added by |
| // MergeSubclassProperties (e.g. by ApacheConfig::AddProperties). |
| delete [] option_id_to_property_array_; |
| option_id_to_property_array_ = |
| new const PropertyBase*[all_properties_->size()]; |
| for (int i = 0, n = all_properties_->size(); i < n; ++i) { |
| option_id_to_property_array_[i] = all_properties_->property(i); |
| } |
| std::sort(option_id_to_property_array_, |
| option_id_to_property_array_ + all_properties_->size(), |
| OptionIdCompare()); |
| } |
| |
| void RewriteOptions::InitOptionNameToPropertyArray() { |
| // This method is called first by Initialize, when base properties are |
| // added, then zero or more times when subclass properties are added by |
| // MergeSubclassProperties (e.g. by ApacheConfig::AddProperties). |
| delete option_name_to_property_map_; |
| option_name_to_property_map_ = new PropertyNameMap; |
| for (int i = 0, n = all_properties_->size(); i < n; ++i) { |
| PropertyBase* prop = all_properties_->property(i); |
| StringPiece name(prop->option_name()); |
| if (!name.empty()) { |
| option_name_to_property_map_->insert(PropertyNameMap::value_type(name, |
| prop)); |
| } |
| } |
| } |
| |
| bool RewriteOptions::Terminate() { |
| if (Properties::Terminate(&properties_)) { |
| DCHECK(option_id_to_property_array_ != NULL); |
| delete [] option_id_to_property_array_; |
| option_id_to_property_array_ = NULL; |
| DCHECK(option_name_to_property_map_ != NULL); |
| option_name_to_property_map_->clear(); |
| delete option_name_to_property_map_; |
| option_name_to_property_map_ = NULL; |
| Properties::Terminate(&all_properties_); |
| return true; |
| } |
| return false; |
| } |
| |
| void RewriteOptions::MergeSubclassProperties(Properties* properties) { |
| all_properties_->Merge(properties); |
| InitOptionIdToPropertyArray(); |
| InitOptionNameToPropertyArray(); |
| } |
| |
| bool RewriteOptions::SetExperimentState(int id) { |
| experiment_id_ = id; |
| return SetupExperimentRewriters(); |
| } |
| |
| void RewriteOptions::SetExperimentStateStr( |
| const StringPiece& experiment_index) { |
| if (experiment_index.length() == 1) { |
| int index = experiment_index[0] - 'a'; |
| int n_experiment_specs = experiment_specs_.size(); |
| if (0 <= index && index < n_experiment_specs) { |
| SetExperimentState(experiment_specs_[index]->id()); |
| } |
| } |
| // Ignore any calls with an invalid index-string. When experiments are ended |
| // a previously valid index string may become invalid. For example, if a |
| // webmaster were running an a/b/c test and now is running an a/b test, a |
| // visitor refreshing an old image opened in a separate tab on the 'c' branch |
| // of the experiment needs to get some version of that image and not an error. |
| // Perhaps more commonly, a webmaster might manually copy a url from pagespeed |
| // output to somewhere else on their site at a time an experiment was active, |
| // and it would be bad to break that resource link when the experiment ended. |
| } |
| |
| GoogleString RewriteOptions::GetExperimentStateStr() const { |
| // Don't look at more than 26 experiment_specs because we use lowercase a-z. |
| // While this is an arbitrary limit, it's much higher than webmasters are |
| // likely to run into in practice. Most of the time people will be running |
| // a/b or a/b/c tests, and an a/b/c/d/.../y/z test would be unwieldy and |
| // difficult to interpret. If this does turn out to be needed we can switch |
| // to base64 to get 64-way tests, and more than one character experiment index |
| // strings would also be possible. |
| for (int i = 0, n = experiment_specs_.size(); i < n && i < 26; ++i) { |
| if (experiment_specs_[i]->id() == experiment_id_) { |
| return GoogleString(1, static_cast<char>('a' + i)); |
| } |
| } |
| return ""; |
| } |
| |
| void RewriteOptions::DisallowTroublesomeResources() { |
| // http://code.google.com/p/modpagespeed/issues/detail?id=38 |
| Disallow("*js_tinyMCE*"); // js_tinyMCE.js |
| // Official tinyMCE URLs: tiny_mce.js, tiny_mce_src.js, tiny_mce_gzip.php, ... |
| Disallow("*tiny_mce*"); |
| // I've also seen tinymce.js |
| Disallow("*tinymce*"); |
| |
| // http://code.google.com/p/modpagespeed/issues/detail?id=352 |
| Disallow("*scriptaculous.js*"); |
| |
| // http://code.google.com/p/modpagespeed/issues/detail?id=186 |
| // ckeditor.js, ckeditor_basic.js, ckeditor_basic_source.js, ... |
| Disallow("*ckeditor*"); |
| |
| // http://code.google.com/p/modpagespeed/issues/detail?id=207 |
| // jquery-ui-1.8.2.custom.min.js, jquery-1.4.4.min.js, jquery.fancybox-... |
| // |
| // TODO(sligocki): Is jquery actually a problem? Perhaps specific |
| // jquery libraries (like tiny MCE). Investigate before disabling. |
| // Disallow("*jquery*"); |
| |
| // http://code.google.com/p/modpagespeed/issues/detail?id=216 |
| // Appears to be an issue with old version of jsminify. |
| // Disallow("*swfobject*"); // swfobject.js |
| |
| // TODO(sligocki): Add disallow for the JS broken in: |
| // http://code.google.com/p/modpagespeed/issues/detail?id=142 |
| // Not clear which JS file is broken and proxying is not working correctly. |
| |
| // Disable lazyload_images if there is another known lazyloader present. |
| DisableLazyloadForClassName("*dfcg*"); |
| DisableLazyloadForClassName("*lazy*"); |
| DisableLazyloadForClassName("*nivo*"); |
| DisableLazyloadForClassName("*slider*"); |
| } |
| |
| // Note: this is not called by default in mod_pagespeed. |
| void RewriteOptions::DisallowResourcesForProxy() { |
| Disallow("*://l.yimg.com/*"); |
| Disallow("*store.yahoo.net/*"); |
| |
| // Changing the url breaks the simpleviewer flash-based slideshow gallery due |
| // to cross domain policy violations. |
| Disallow("*simpleviewer.js*"); |
| |
| // Disable resources that are already being shared across multiple sites and |
| // have strong CDN support (ie they are already cheap to fetch and are also |
| // very likely to reside in the browser cache from visits to another site). |
| // We keep these patterns as specific as possible while avoiding internal |
| // wildcards. Note that all of these urls have query parameters in long-tail |
| // requests. |
| // Do allow these to be inlined; if they're small enough it can be better to |
| // inline them then fetch them from cache, and they're not always in cache. |
| // TODO(jmaessen): Consider setting up the blacklist by domain name and using |
| // regexps only after a match has been found. Alternatively, since we're |
| // setting up a binary choice here, consider using RE2 to make the yes/no |
| // decision. |
| AllowOnlyWhenInlining("*//ajax.googleapis.com/ajax/libs/*.js*"); |
| AllowOnlyWhenInlining("*//pagead2.googlesyndication.com/pagead/show_ads.js*"); |
| AllowOnlyWhenInlining( |
| "*//partner.googleadservices.com/gampad/google_service.js*"); |
| AllowOnlyWhenInlining("*//platform.twitter.com/widgets.js*"); |
| AllowOnlyWhenInlining("*//s7.addthis.com/js/250/addthis_widget.js*"); |
| AllowOnlyWhenInlining("*//www.google.com/coop/cse/brand*"); |
| AllowOnlyWhenInlining("*//www.google-analytics.com/urchin.js*"); |
| AllowOnlyWhenInlining("*//www.googleadservices.com/pagead/conversion.js*"); |
| AllowOnlyWhenInlining("*connect.facebook.net/*"); |
| } |
| |
| bool RewriteOptions::EnableFiltersByCommaSeparatedList( |
| const StringPiece& filters, MessageHandler* handler) { |
| return AddCommaSeparatedListToFilterSetState( |
| filters, &enabled_filters_, handler); |
| } |
| |
| bool RewriteOptions::DisableFiltersByCommaSeparatedList( |
| const StringPiece& filters, MessageHandler* handler) { |
| return AddCommaSeparatedListToFilterSetState( |
| filters, &disabled_filters_, handler); |
| } |
| |
| bool RewriteOptions::ForbidFiltersByCommaSeparatedList( |
| const StringPiece& filters, MessageHandler* handler) { |
| return AddCommaSeparatedListToFilterSetState( |
| filters, &forbidden_filters_, handler); |
| } |
| |
| void RewriteOptions::DisableAllFilters() { |
| DCHECK(!frozen_); |
| modified_ = true; |
| enabled_filters_.clear(); |
| SetRewriteLevel(RewriteOptions::kPassThrough); |
| disabled_filters_.SetAll(); |
| } |
| |
| void RewriteOptions::DisableAllFiltersNotExplicitlyEnabled() { |
| modified_ |= disabled_filters_.MergeInverted(enabled_filters_); |
| } |
| |
| void RewriteOptions::EnableFilter(Filter filter) { |
| DCHECK(!frozen_); |
| modified_ |= enabled_filters_.Insert(filter); |
| } |
| |
| void RewriteOptions::SoftEnableFilterForTesting(Filter filter) { |
| // If we're already in 'all filters mode', then just enable the specified |
| // filter. |
| if (level_.value() == RewriteOptions::kAllFilters) { |
| disabled_filters_.Erase(filter); |
| forbidden_filters_.Erase(filter); |
| } else { |
| // Keep track of any filters that were enabled already. |
| RewriteOptions::FilterSet already_enabled; |
| already_enabled.Insert(filter); |
| for (int i = 0; i < RewriteOptions::kEndOfFilters; ++i) { |
| RewriteOptions::Filter filter = static_cast<RewriteOptions::Filter>(i); |
| if (Enabled(filter)) { |
| already_enabled.Insert(filter); |
| } |
| } |
| |
| SetRewriteLevel(RewriteOptions::kAllFilters); |
| for (int i = 0; i < RewriteOptions::kEndOfFilters; ++i) { |
| RewriteOptions::Filter filter = static_cast<RewriteOptions::Filter>(i); |
| if (!already_enabled.IsSet(filter)) { |
| DisableFilter(filter); |
| } |
| } |
| } |
| } |
| |
| void RewriteOptions::ForceEnableFilter(Filter filter) { |
| DCHECK(!frozen_); |
| |
| // insert into set of enabled filters. |
| modified_ |= enabled_filters_.Insert(filter); |
| |
| // remove from set of disabled filters. |
| modified_ |= disabled_filters_.Erase(filter); |
| |
| // remove from set of forbidden filters. |
| modified_ |= forbidden_filters_.Erase(filter); |
| } |
| |
| void RewriteOptions::DistributeFiltersByCommaSeparatedList( |
| const StringPiece& filters, MessageHandler* handler) { |
| StringPieceVector names; |
| SplitStringPieceToVector(filters, ",", &names, true); |
| for (int i = 0, n = names.size(); i < n; ++i) { |
| DistributeFilter(names[i]); |
| } |
| } |
| |
| void RewriteOptions::DistributeFilter(const StringPiece& filter_id) { |
| DCHECK(!frozen_); |
| std::pair<FilterIdSet::iterator, bool> inserted = |
| distributable_filters_.insert(filter_id.as_string()); |
| modified_ |= inserted.second; |
| } |
| |
| bool RewriteOptions::Distributable(const StringPiece& filter_id) const { |
| return distributable_filters_.find(filter_id.as_string()) |
| != distributable_filters_.end(); |
| } |
| |
| void RewriteOptions::EnableExtendCacheFilters() { |
| EnableFilter(kExtendCacheCss); |
| EnableFilter(kExtendCacheImages); |
| EnableFilter(kExtendCacheScripts); |
| // Doesn't enable kExtendCachePdfs. |
| } |
| |
| void RewriteOptions::DisableFilter(Filter filter) { |
| DCHECK(!frozen_); |
| modified_ |= disabled_filters_.Insert(filter); |
| } |
| |
| void RewriteOptions::ForbidFilter(Filter filter) { |
| DCHECK(!frozen_); |
| modified_ |= forbidden_filters_.Insert(filter); |
| } |
| |
| void RewriteOptions::EnableFilters( |
| const RewriteOptions::FilterSet& filter_set) { |
| modified_ |= enabled_filters_.Merge(filter_set); |
| } |
| |
| void RewriteOptions::DisableFilters( |
| const RewriteOptions::FilterSet& filter_set) { |
| modified_ |= disabled_filters_.Merge(filter_set); |
| } |
| |
| void RewriteOptions::ForbidFilters( |
| const RewriteOptions::FilterSet& filter_set) { |
| modified_ |= forbidden_filters_.Merge(filter_set); |
| } |
| |
| void RewriteOptions::ClearFilters() { |
| DCHECK(!frozen_); |
| modified_ = true; |
| enabled_filters_.clear(); |
| disabled_filters_.clear(); |
| forbidden_filters_.clear(); |
| |
| // Re-enable HtmlWriterFilter by default. |
| EnableFilter(kHtmlWriterFilter); |
| } |
| |
| bool RewriteOptions::AddCommaSeparatedListToFilterSetState( |
| const StringPiece& filters, FilterSet* set, MessageHandler* handler) { |
| DCHECK(!frozen_); |
| size_t prev_set_size = set->size(); |
| bool ret = AddCommaSeparatedListToFilterSet(filters, set, handler); |
| modified_ |= (set->size() != prev_set_size); |
| return ret; |
| } |
| |
| bool RewriteOptions::AddCommaSeparatedListToFilterSet( |
| const StringPiece& filters, FilterSet* set, MessageHandler* handler) { |
| StringPieceVector names; |
| SplitStringPieceToVector(filters, ",", &names, true); |
| bool ret = true; |
| for (int i = 0, n = names.size(); i < n; ++i) { |
| ret = AddByNameToFilterSet(names[i], set, handler); |
| } |
| return ret; |
| } |
| |
| bool RewriteOptions::AdjustFiltersByCommaSeparatedList( |
| const StringPiece& filters, MessageHandler* handler) { |
| DCHECK(!frozen_); |
| StringPieceVector names; |
| SplitStringPieceToVector(filters, ",", &names, true); |
| bool ret = true; |
| size_t sets_size_sum_before = |
| (enabled_filters_.size() + disabled_filters_.size()); |
| |
| // Default to false unless no filters are specified. |
| // "PageSpeedFilters=" -> disable all filters. |
| bool non_incremental = names.empty(); |
| for (int i = 0, n = names.size(); i < n; ++i) { |
| StringPiece& option = names[i]; |
| TrimWhitespace(&option); |
| if (!option.empty()) { |
| if (option[0] == '-') { |
| option.remove_prefix(1); |
| ret = AddByNameToFilterSet(names[i], &disabled_filters_, handler); |
| } else if (option[0] == '+') { |
| option.remove_prefix(1); |
| ret = AddByNameToFilterSet(names[i], &enabled_filters_, handler); |
| } else { |
| // No prefix means: reset to pass-through mode prior to |
| // applying any of the filters. +a,-b,+c" will just add |
| // a and c and remove b to current default config, but |
| // "+a,-b,+c,d" will just run with filters a, c and d. |
| ret = AddByNameToFilterSet(names[i], &enabled_filters_, handler); |
| non_incremental = true; |
| } |
| } |
| } |
| |
| if (non_incremental) { |
| SetRewriteLevel(RewriteOptions::kPassThrough); |
| DisableAllFiltersNotExplicitlyEnabled(); |
| modified_ = true; |
| } else { |
| // TODO(jmarantz): this modified_ computation for query-params doesn't |
| // work as we'd like in RewriteQueryTest.NoChangesShouldNotModify. See |
| // a more detailed TODO there. |
| size_t sets_size_sum_after = |
| (enabled_filters_.size() + disabled_filters_.size()); |
| modified_ |= (sets_size_sum_before != sets_size_sum_after); |
| } |
| return ret; |
| } |
| |
| bool RewriteOptions::AddByNameToFilterSet( |
| const StringPiece& option, FilterSet* set, MessageHandler* handler) { |
| bool ret = true; |
| Filter filter = LookupFilter(option); |
| if (filter == kEndOfFilters) { |
| // Handle a compound filter name. This is much less common, so we don't |
| // have any special infrastructure for it; just code. |
| // WARNING: Be careful if you add things here; the filters you add |
| // here will be invokable by outside people, so they better not crash |
| // if that happens! |
| if (option == "rewrite_images") { |
| // Every filter here needs to be listed in kCoreFilterSet as well. |
| set->Insert(kConvertGifToPng); |
| set->Insert(kConvertJpegToProgressive); |
| set->Insert(kConvertJpegToWebp); |
| set->Insert(kConvertPngToJpeg); |
| set->Insert(kConvertToWebpLossless); |
| set->Insert(kInlineImages); |
| set->Insert(kJpegSubsampling); |
| set->Insert(kRecompressJpeg); |
| set->Insert(kRecompressPng); |
| set->Insert(kRecompressWebp); |
| set->Insert(kResizeImages); |
| set->Insert(kStripImageColorProfile); |
| set->Insert(kStripImageMetaData); |
| } else if (option == "recompress_images") { |
| // Every filter here needs to be listed under "rewrite_images" as well. |
| set->Insert(kConvertGifToPng); |
| set->Insert(kConvertJpegToProgressive); |
| set->Insert(kConvertJpegToWebp); |
| set->Insert(kJpegSubsampling); |
| set->Insert(kRecompressJpeg); |
| set->Insert(kRecompressPng); |
| set->Insert(kRecompressWebp); |
| set->Insert(kStripImageColorProfile); |
| set->Insert(kStripImageMetaData); |
| } else if (option == "extend_cache") { |
| // Every filter here needs to be listed in kCoreFilterSet as well. |
| set->Insert(kExtendCacheCss); |
| set->Insert(kExtendCacheImages); |
| set->Insert(kExtendCacheScripts); |
| } else if (option == "rewrite_javascript") { |
| // Every filter here needs to be listed in kCoreFilterSet and |
| // kOptimizeForBandwidthFilterSet. Note that kRewriteJavascriptExternal |
| // makes sense in OptimizeForBandwidth because we start rewriting |
| // external JS files when we parse them in HTML, so that they are ready |
| // in cache for the IPRO request, even though we will not mutate the |
| // URLs in HTML. |
| set->Insert(kRewriteJavascriptExternal); |
| set->Insert(kRewriteJavascriptInline); |
| } else if (option == "testing") { |
| for (int i = 0, n = arraysize(kTestFilterSet); i < n; ++i) { |
| set->Insert(kTestFilterSet[i]); |
| } |
| for (int i = 0, n = arraysize(kCoreFilterSet); i < n; ++i) { |
| set->Insert(kCoreFilterSet[i]); |
| } |
| } else if (option == "core") { |
| for (int i = 0, n = arraysize(kCoreFilterSet); i < n; ++i) { |
| set->Insert(kCoreFilterSet[i]); |
| } |
| } else { |
| if (handler != NULL) { |
| handler->Message(kWarning, "Invalid filter name: %s", |
| option.as_string().c_str()); |
| } |
| ret = false; |
| } |
| } else { |
| set->Insert(filter); |
| // kResizeMobileImages requires kDelayImages. |
| if (filter == kResizeMobileImages) { |
| set->Insert(kDelayImages); |
| } |
| } |
| return ret; |
| } |
| |
| bool RewriteOptions::AddCommaSeparatedListToOptionSet( |
| const StringPiece& options, OptionSet* set, MessageHandler* handler) { |
| StringPieceVector option_vector; |
| bool ret = true; |
| SplitStringPieceToVector(options, ",", &option_vector, true); |
| for (int i = 0, n = option_vector.size(); i < n; ++i) { |
| StringPieceVector single_option_and_value; |
| SplitStringPieceToVector(option_vector[i], "=", &single_option_and_value, |
| true); |
| if (single_option_and_value.size() == 2) { |
| set->insert(OptionStringPair(single_option_and_value[0].as_string(), |
| single_option_and_value[1].as_string())); |
| } else { |
| ret = false; |
| } |
| } |
| return ret; |
| } |
| |
| RewriteOptions::Filter RewriteOptions::LookupFilterById( |
| const StringPiece& filter_id) { |
| GoogleString key(filter_id.data(), filter_id.size()); |
| |
| FilterEnumToIdAndNameEntry entry; |
| entry.filter_enum = kEndOfFilters; |
| entry.filter_id = key.c_str(); |
| entry.filter_name = ""; |
| const FilterEnumToIdAndNameEntry** it = std::lower_bound( |
| filter_id_to_enum_array_, |
| filter_id_to_enum_array_ + arraysize(filter_id_to_enum_array_), |
| &entry, |
| RewriteOptions::FilterEnumToIdAndNameEntryLessThanById); |
| // We use lower_bound because it's O(log n) so relatively efficient. It |
| // returns a pointer to the entry whose id is >= filter_id; if filter_id is |
| // higher than all ids then 'it' will point past the end, otherwise we have |
| // to check that the ids actually match. |
| if (it == filter_id_to_enum_array_ + arraysize(filter_id_to_enum_array_) || |
| filter_id != (*it)->filter_id) { |
| return kEndOfFilters; |
| } |
| return (*it)->filter_enum; |
| } |
| |
| const RewriteOptions::PropertyBase* RewriteOptions::LookupOptionById( |
| StringPiece option_id) { |
| const PropertyBase** end = |
| option_id_to_property_array_ + all_properties_->size(); |
| const PropertyBase** it = std::lower_bound( |
| option_id_to_property_array_, end, option_id, OptionIdCompare()); |
| // We use lower_bound because it's O(log n) so relatively efficient, but |
| // we must double-check its result as it doesn't guarantee an exact match. |
| // Note that std::binary_search provides an exact match but only a bool |
| // result and not the actual object we were searching for. |
| return ((it == end || option_id != (*it)->id()) ? NULL : *it); |
| } |
| |
| const RewriteOptions::PropertyBase* RewriteOptions::LookupOptionByName( |
| StringPiece option_name) { |
| // There are many options without a name, and it doesn't make sense to |
| // find "the one" with an empty name, so short-circuit that early. |
| if (option_name.empty()) { |
| return NULL; |
| } |
| PropertyNameMap::iterator |
| end = option_name_to_property_map_->end(), |
| pos = option_name_to_property_map_->find( |
| GetEffectiveOptionName(option_name)); |
| return (pos == end ? NULL : pos->second); |
| } |
| |
| const StringPiece RewriteOptions::LookupOptionNameById(StringPiece option_id) { |
| const PropertyBase* option = LookupOptionById(option_id); |
| return (option == NULL ? StringPiece() : option->option_name()); |
| } |
| |
| bool RewriteOptions::IsValidOptionName(StringPiece name) { |
| return (LookupOptionByName(name) != NULL); |
| } |
| |
| bool RewriteOptions::SetOptionsFromName(const OptionSet& option_set, |
| MessageHandler* handler) { |
| bool ret = true; |
| for (RewriteOptions::OptionSet::const_iterator iter = option_set.begin(); |
| iter != option_set.end(); ++iter) { |
| GoogleString msg; |
| OptionSettingResult result = SetOptionFromName( |
| iter->first, iter->second, &msg); |
| if (result != kOptionOk) { |
| handler->Message( |
| kWarning, "Failed to set %s to %s (%s)", |
| iter->first.c_str(), iter->second.c_str(), msg.c_str()); |
| ret = false; |
| } |
| } |
| return ret; |
| } |
| |
| RewriteOptions::OptionSettingResult RewriteOptions::SetOptionFromName( |
| StringPiece name, StringPiece value, GoogleString* msg) { |
| GoogleString error_detail; |
| OptionSettingResult result = SetOptionFromNameInternal( |
| name, value, RewriteOptions::kProcessScopeStrict /* max_scope*/, |
| &error_detail); |
| return FormatSetOptionMessage(result, name, value, error_detail, msg); |
| } |
| |
| RewriteOptions::OptionSettingResult RewriteOptions::SetOptionFromName( |
| StringPiece name, StringPiece value) { |
| GoogleString error_detail; |
| return SetOptionFromNameInternal( |
| name, value, RewriteOptions::kProcessScopeStrict /* max_scope */, |
| &error_detail); |
| } |
| |
| RewriteOptions::OptionSettingResult RewriteOptions::SetOptionFromQuery( |
| StringPiece name, StringPiece value) { |
| GoogleString error_detail; |
| return SetOptionFromNameInternal( |
| name, value, RewriteOptions::kQueryScope /* max_scope */, &error_detail); |
| } |
| |
| RewriteOptions::OptionSettingResult RewriteOptions::SetOptionFromRemoteConfig( |
| StringPiece name, StringPiece value) { |
| GoogleString error_detail; |
| return SetOptionFromNameInternal( |
| name, value, RewriteOptions::kDirectoryScope, &error_detail); |
| } |
| |
| RewriteOptions::OptionSettingResult RewriteOptions::FormatSetOptionMessage( |
| OptionSettingResult result, StringPiece name, StringPiece value, |
| StringPiece error_detail, GoogleString* msg) { |
| if (!IsValidOptionName(name)) { |
| // Not a mapped option. |
| SStringPrintf(msg, "Option %s not mapped.", name.as_string().c_str()); |
| return kOptionNameUnknown; |
| } |
| switch (result) { |
| case kOptionNameUnknown: |
| SStringPrintf(msg, "Option %s not found.", name.as_string().c_str()); |
| break; |
| case kOptionValueInvalid: |
| SStringPrintf(msg, "Cannot set option %s to %s. %s", |
| name.as_string().c_str(), value.as_string().c_str(), |
| error_detail.as_string().c_str()); |
| break; |
| default: |
| break; |
| } |
| return result; |
| } |
| |
| RewriteOptions::OptionSettingResult RewriteOptions::ParseAndSetOptionFromName1( |
| StringPiece name, StringPiece arg, GoogleString* msg, |
| MessageHandler* handler) { |
| // Parse and set with the equvalent of "query = false". |
| return ParseAndSetOptionFromNameWithScope( |
| name, arg, RewriteOptions::kProcessScopeStrict, msg, handler); |
| } |
| |
| RewriteOptions::OptionSettingResult |
| RewriteOptions::ParseAndSetOptionFromNameWithScope( |
| StringPiece name, StringPiece arg, RewriteOptions::OptionScope max_scope, |
| GoogleString* msg, MessageHandler* handler) { |
| GoogleString error_detail; |
| OptionSettingResult result = |
| SetOptionFromNameInternal(name, arg, max_scope, &error_detail); |
| if (result != RewriteOptions::kOptionNameUnknown) { |
| return FormatSetOptionMessage(result, name, arg, error_detail, msg); |
| } |
| |
| // Assume all goes well; if not, set result accordingly. |
| result = RewriteOptions::kOptionOk; |
| |
| // TODO(matterbury): use a hash map for faster lookup/switching. |
| if (StringCaseEqual(name, kAllow)) { |
| Allow(arg); |
| } else if (StringCaseEqual(name, kDisableFilters)) { |
| if (!DisableFiltersByCommaSeparatedList(arg, handler)) { |
| *msg = "Failed to disable some filters."; |
| result = RewriteOptions::kOptionValueInvalid; |
| } |
| } else if (StringCaseEqual(name, kDisallow)) { |
| Disallow(arg); |
| } else if (StringCaseEqual(name, kDistributableFilters)) { |
| DistributeFiltersByCommaSeparatedList(arg, handler); |
| } else if (StringCaseEqual(name, kDomain)) { |
| WriteableDomainLawyer()->AddDomain(arg, handler); |
| } else if (StringCaseEqual(name, kProxySuffix)) { |
| WriteableDomainLawyer()->set_proxy_suffix(arg.as_string()); |
| } else if (StringCaseEqual(name, kDownstreamCachePurgeLocationPrefix)) { |
| GoogleUrl gurl(arg); |
| if (gurl.IsWebValid()) { |
| // The host:port location where purge requests are to be sent should |
| // be made "known" to the DomainLawyer so that when the |
| // LoopbackRouteFetcher tries to send the request, it does not consider |
| // this an invalid domain. |
| WriteableDomainLawyer()->AddKnownDomain(gurl.HostAndPort(), handler); |
| set_downstream_cache_purge_location_prefix(arg); |
| } else { |
| *msg = "Downstream cache purge location prefix is invalid."; |
| result = RewriteOptions::kOptionValueInvalid; |
| } |
| } else if (StringCaseEqual(name, kEnableFilters)) { |
| if (!EnableFiltersByCommaSeparatedList(arg, handler)) { |
| *msg = "Failed to enable some filters."; |
| result = RewriteOptions::kOptionValueInvalid; |
| } |
| } else if (StringCaseEqual(name, kExperimentVariable)) { |
| int slot; |
| if (!StringToInt(arg, &slot) || slot < 1 || slot > 5) { |
| *msg = "must be an integer between 1 and 5"; |
| result = RewriteOptions::kOptionValueInvalid; |
| } else { |
| set_experiment_ga_slot(slot); |
| } |
| } else if (StringCaseEqual(name, kExperimentSpec)) { |
| ExperimentSpec* spec = AddExperimentSpec(arg, handler); |
| if (spec == NULL) { |
| *msg = "not a valid experiment spec"; |
| result = RewriteOptions::kOptionValueInvalid; |
| } else { |
| // To test the validity of options in the experiment spec we have to apply |
| // them to a RewriteOptions. Try to apply them now, so if there are |
| // configuration errors we can report them early instead of on each |
| // request. |
| scoped_ptr<RewriteOptions> clone(Clone()); |
| if (!clone->SetOptionsFromName(spec->filter_options(), handler)) { |
| *msg = "experiment spec has invalid options= component"; |
| result = RewriteOptions::kOptionValueInvalid; |
| } |
| } |
| } else if (StringCaseEqual(name, kForbidFilters)) { |
| if (!ForbidFiltersByCommaSeparatedList(arg, handler)) { |
| *msg = "Failed to forbid some filters."; |
| result = RewriteOptions::kOptionValueInvalid; |
| } |
| } else if (StringCaseEqual(name, kRetainComment)) { |
| RetainComment(arg); |
| } else if (StringCaseEqual(name, kBlockingRewriteRefererUrls)) { |
| EnableBlockingRewriteForRefererUrlPattern(arg); |
| } else { |
| result = RewriteOptions::kOptionNameUnknown; |
| } |
| return result; |
| } |
| |
| RewriteOptions::OptionSettingResult RewriteOptions::ParseAndSetOptionFromName2( |
| StringPiece name, StringPiece arg1, StringPiece arg2, |
| GoogleString* msg, MessageHandler* handler) { |
| // Assume all goes well; if not, set result accordingly. |
| OptionSettingResult result = RewriteOptions::kOptionOk; |
| |
| // TODO(matterbury): use a hash map for faster lookup/switching. |
| if (StringCaseEqual(name, kCustomFetchHeader)) { |
| AddCustomFetchHeader(arg1, arg2); |
| } else if (StringCaseEqual(name, kLoadFromFile)) { |
| file_load_policy()->Associate(arg1, arg2); |
| } else if (StringCaseEqual(name, kLoadFromFileMatch)) { |
| if (!file_load_policy()->AssociateRegexp(arg1, arg2, msg)) { |
| result = RewriteOptions::kOptionValueInvalid; |
| } |
| } else if (StringCaseEqual(name, kLoadFromFileRule) || |
| StringCaseEqual(name, kLoadFromFileRuleMatch)) { |
| bool is_regexp = (name == kLoadFromFileRuleMatch); |
| bool allow; |
| if (StringCaseEqual(arg1, "Allow")) { |
| allow = true; |
| } else if (StringCaseEqual(arg1, "Disallow")) { |
| allow = false; |
| } else { |
| *msg = "Argument 1 must be either 'Allow' or 'Disallow'"; |
| return RewriteOptions::kOptionValueInvalid; |
| } |
| if (!file_load_policy()->AddRule(arg2.as_string(), |
| is_regexp, allow, msg)) { |
| result = RewriteOptions::kOptionValueInvalid; |
| } |
| } else if (StringCaseEqual(name, kMapOriginDomain)) { |
| WriteableDomainLawyer()->AddOriginDomainMapping(arg1, arg2, "", handler); |
| } else if (StringCaseEqual(name, kMapProxyDomain)) { |
| WriteableDomainLawyer()->AddProxyDomainMapping(arg1, arg2, "", handler); |
| } else if (StringCaseEqual(name, kMapRewriteDomain)) { |
| WriteableDomainLawyer()->AddRewriteDomainMapping(arg1, arg2, handler); |
| } else if (StringCaseEqual(name, kShardDomain)) { |
| if (!arg2.empty()) { |
| // We allow people to put: |
| // pagespeed ShardDomain domain_to_shard ""; |
| // because we want people to be able to use script variables in nginx to |
| // disable domain sharding with spdy/http2. |
| // See pagespeed/module/https_support#h2_configuration_nginx |
| WriteableDomainLawyer()->AddShard(arg1, arg2, handler); |
| } |
| } else { |
| result = RewriteOptions::kOptionNameUnknown; |
| } |
| return result; |
| } |
| |
| RewriteOptions::OptionSettingResult RewriteOptions::ParseAndSetOptionFromName3( |
| StringPiece name, StringPiece arg1, StringPiece arg2, StringPiece arg3, |
| GoogleString* msg, MessageHandler* handler) { |
| // Assume all goes well; if not, set result accordingly. |
| OptionSettingResult result = RewriteOptions::kOptionOk; |
| if (StringCaseEqual(name, kUrlValuedAttribute)) { |
| // Examples: |
| // UrlValuedAttribute span src Hyperlink |
| // - <span src=...> indicates a hyperlink |
| // UrlValuedAttribute hr imgsrc Image |
| // - <hr image=...> indicates an image resource |
| semantic_type::Category category; |
| if (!semantic_type::ParseCategory(arg3, &category)) { |
| *msg = StrCat("Invalid resource category: ", arg3); |
| result = RewriteOptions::kOptionValueInvalid; |
| } else { |
| AddUrlValuedAttribute(arg1, arg2, category); |
| } |
| } else if (StringCaseEqual(name, kLibrary)) { |
| // Library bytes md5 canonical_url |
| // Examples: |
| // Library 43567 5giEj_jl-Ag5G8 http://www.example.com/url.js |
| int64 bytes; |
| if (!StringToInt64(arg1, &bytes) || bytes < 0) { |
| *msg = "Library size must be a positive 64-bit integer"; |
| result = RewriteOptions::kOptionValueInvalid; |
| } else if (!RegisterLibrary(bytes, arg2, arg3)) { |
| *msg = StrCat("Format is size md5 url; bad md5 ", arg2, " or URL ", arg3); |
| result = RewriteOptions::kOptionValueInvalid; |
| } |
| } else if (StringCaseEqual(name, kMapOriginDomain)) { |
| WriteableDomainLawyer()->AddOriginDomainMapping(arg1, arg2, arg3, handler); |
| } else if (StringCaseEqual(name, kMapProxyDomain)) { |
| WriteableDomainLawyer()->AddProxyDomainMapping(arg1, arg2, arg3, handler); |
| } else { |
| result = RewriteOptions::kOptionNameUnknown; |
| } |
| return result; |
| } |
| |
| StringPiece RewriteOptions::GetEffectiveOptionName(StringPiece name) { |
| StringPiece effective_name = name; |
| std::vector<DeprecatedOptionMap>::iterator id = |
| std::lower_bound(kDeprecatedOptionNameList.begin(), |
| kDeprecatedOptionNameList.end(), |
| name, |
| DeprecatedOptionMap::LessThan); |
| if (id != kDeprecatedOptionNameList.end() && |
| StringCaseEqual(name, id->deprecated_option_name)) { |
| effective_name = id->new_option_name; |
| } |
| return effective_name; |
| } |
| |
| RewriteOptions::OptionSettingResult |
| RewriteOptions::SetOptionFromNameInternal( |
| StringPiece name, StringPiece value, RewriteOptions::OptionScope max_scope, |
| GoogleString* error_detail) { |
| if (!IsValidOptionName(name)) { |
| return kOptionNameUnknown; |
| } |
| StringPiece effective_name = GetEffectiveOptionName(name); |
| OptionBaseVector::iterator it = |
| std::lower_bound(all_options_.begin(), all_options_.end(), effective_name, |
| RewriteOptions::OptionNameLessThanArg); |
| if (it != all_options_.end()) { |
| OptionBase* option = *it; |
| if (StringCaseEqual(effective_name, option->option_name())) { |
| if (option->scope() > max_scope) { |
| StrAppend(error_detail, "Option ", name, |
| " cannot be set. Maximum allowed scope is ", |
| ScopeEnumToString(max_scope)); |
| return kOptionNameUnknown; |
| } else if (!option->SetFromString(value, error_detail)) { |
| return kOptionValueInvalid; |
| } else { |
| return kOptionOk; |
| } |
| } |
| } |
| return kOptionNameUnknown; |
| } |
| |
| bool RewriteOptions::OptionValue(StringPiece name, |
| const char** id, |
| bool* was_set, |
| GoogleString* value) const { |
| OptionBaseVector::const_iterator it = std::lower_bound( |
| all_options_.begin(), all_options_.end(), name, |
| RewriteOptions::OptionNameLessThanArg); |
| if (it != all_options_.end()) { |
| OptionBase* option = *it; |
| if (StringCaseEqual(name, option->option_name())) { |
| *value = option->ToString(); |
| *id = option->id(); |
| *was_set = option->was_set(); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| bool RewriteOptions::SetOptionFromNameAndLog(StringPiece name, |
| StringPiece value, |
| MessageHandler* handler) { |
| GoogleString msg; |
| OptionSettingResult result = SetOptionFromName(name, value, &msg); |
| if (result == kOptionOk) { |
| return true; |
| } else { |
| handler->MessageS(kWarning, msg); |
| return false; |
| } |
| } |
| |
| bool RewriteOptions::ParseFromString(StringPiece value_string, |
| bool* value) { |
| // How are bools passed in the string? I am assuming "true"/"false" or |
| // "on"/"off". |
| if (StringCaseEqual(value_string, "true") || |
| StringCaseEqual(value_string, "on")) { |
| *value = true; |
| } else if (StringCaseEqual(value_string, "false") || |
| StringCaseEqual(value_string, "off")) { |
| *value = false; |
| } else { |
| // value_string is not "true"/"false" or "on"/"off". Return a parse |
| // error. |
| return false; |
| } |
| return true; |
| } |
| |
| bool RewriteOptions::ParseFromString(StringPiece value_string, |
| EnabledEnum* value) { |
| bool bool_value; |
| if (ParseFromString(value_string, &bool_value)) { |
| *value = bool_value ? kEnabledOn : kEnabledOff; |
| } else if (StringCaseEqual(value_string, "unplugged")) { |
| *value = kEnabledUnplugged; |
| } else { |
| // value_string is not "true"/"false" or "on"/"off"/"unplugged". |
| // Return a parse error. |
| return false; |
| } |
| return true; |
| } |
| |
| bool RewriteOptions::ParseFromString(StringPiece value_string, |
| ResponsiveDensities* value) { |
| // Temp vector so that we don't return any densities if there's an error. |
| std::vector<double> ret; |
| StringPieceVector density_strs; |
| // Ignores empty strings. |
| SplitStringUsingSubstr(value_string, ",", &density_strs); |
| if (density_strs.size() == 0) { |
| // TODO(sligocki): Return error_message instead of directly logging message. |
| LOG(ERROR) << "ResponsiveImageDensities: Must not be empty list."; |
| return false; |
| } |
| for (size_t i = 0, n = density_strs.size(); i < n; ++i) { |
| double density; |
| if (!ParseFromString(density_strs[i], &density)) { |
| LOG(ERROR) << "ResponsiveImageDensities: Cannot parse number: " |
| << density_strs[i]; |
| return false; |
| } else if (density <= 0) { |
| LOG(ERROR) << "ResponsiveImageDensities: Must be > 0. Invalid number: " |
| << density_strs[i]; |
| return false; |
| } else { // Valid |
| ret.push_back(density); |
| } |
| } |
| value->swap(ret); |
| std::sort(value->begin(), value->end()); |
| return true; |
| } |
| |
| bool RewriteOptions::ParseFromString(StringPiece value_string, |
| protobuf::MessageLite* proto) { |
| return ParseProtoFromStringPiece(value_string, proto); |
| } |
| |
| bool RewriteOptions::ParseFromString(StringPiece value_string, |
| AllowVaryOn* allow_vary_on) { |
| AllowVaryOn allow; |
| TrimWhitespace(&value_string); |
| if (StringCaseEqual(value_string, AllowVaryOn::kNoneString)) { |
| // "allow" has already been initialized to all false; nothing to do. |
| } else if (StringCaseEqual(value_string, AllowVaryOn::kAutoString)) { |
| allow.set_allow_auto(true); |
| } else { |
| StringPieceVector value_vector; |
| SplitStringPieceToVector(value_string, ",", &value_vector, |
| false /* omit_empty_strings */); |
| // When "value_string" is empty, "value_vector" has only one element |
| // which is an empty string. |
| for (size_t i = 0, n = value_vector.size(); i < n; ++i) { |
| StringPiece value = value_vector[i]; |
| TrimWhitespace(&value); |
| if (StringCaseEqual(value, HttpAttributes::kAccept)) { |
| allow.set_allow_accept(true); |
| } else if (StringCaseEqual(value, HttpAttributes::kSaveData)) { |
| allow.set_allow_save_data(true); |
| } else if (StringCaseEqual(value, HttpAttributes::kUserAgent)) { |
| allow.set_allow_user_agent(true); |
| } else { |
| return false; |
| } |
| } |
| } |
| *allow_vary_on = allow; |
| return true; |
| } |
| |
| bool RewriteOptions::Enabled(Filter filter) const { |
| // Enforce a hierarchy of configuration precedence: |
| // a. Explicit forbid is permanent all the way down the hierarchy and |
| // cannot be overridden |
| // b. "lower level" configs (vhost, query-params, subdirectories) override |
| // higher level -- this takes place in Merge. |
| // c. explicit filter setting overrides preserve |
| // d. preserve overrides rewrite-level |
| // |
| // TODO(jmarantz): add doc explaining this. |
| |
| // Explicitly disabled filters always lose, independent of level & preserve. |
| if (disabled_filters_.IsSet(filter) || forbidden_filters_.IsSet(filter)) { |
| return false; |
| } |
| |
| // Explicitly enabled filters always win, independent of preserve. |
| if (enabled_filters_.IsSet(filter)) { |
| return true; |
| } |
| |
| FilterProperties properties = filter_properties[filter]; |
| if (css_preserve_urls() && properties.preserve_css_urls) { |
| return false; |
| } |
| if (js_preserve_urls() && properties.preserve_js_urls) { |
| return false; |
| } |
| if (image_preserve_urls() && properties.preserve_image_urls) { |
| return false; |
| } |
| |
| switch (level_.value()) { |
| case kTestingCoreFilters: |
| if (properties.level_test) { |
| return true; |
| } |
| FALLTHROUGH_INTENDED; |
| case kCoreFilters: |
| if (properties.level_core) { |
| return true; |
| } |
| break; |
| case kOptimizeForBandwidth: |
| if (properties.level_optimize_for_bandwidth) { |
| return true; |
| } |
| break; |
| case kMobilizeFilters: |
| if (properties.level_mobilize) { |
| return true; |
| } |
| break; |
| case kAllFilters: |
| if (!properties.level_dangerous) { |
| return true; |
| } |
| break; |
| case kPassThrough: |
| break; |
| } |
| return false; |
| } |
| |
| bool RewriteOptions::Forbidden(Filter filter) const { |
| return (forbidden_filters_.IsSet(filter) || |
| (forbid_all_disabled_filters() && disabled_filters_.IsSet(filter))); |
| } |
| |
| bool RewriteOptions::Forbidden(StringPiece filter_id) const { |
| // It's forbidden if it's expressly forbidden or if it's disabled and all |
| // disabled filters are forbidden. |
| RewriteOptions::Filter filter = RewriteOptions::LookupFilterById(filter_id); |
| // TODO(jmarantz): handle "ce" which is not indexed as a single filter. |
| return ((filter != kEndOfFilters) && Forbidden(filter)); |
| } |
| |
| bool RewriteOptions::HasRejectedHeader( |
| const StringPiece& header_name, |
| const RequestHeaders* request_headers) const { |
| ConstStringStarVector header_values; |
| if (request_headers->Lookup(header_name, &header_values)) { |
| for (int i = 0, n = header_values.size(); i < n; ++i) { |
| if (IsRejectedRequest(header_name, *header_values[i])) { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| bool RewriteOptions::IsRequestDeclined( |
| const GoogleString& url, |
| const RequestHeaders* request_headers) const { |
| if (IsRejectedUrl(url) || |
| HasRejectedHeader(HttpAttributes::kUserAgent, request_headers) || |
| HasRejectedHeader(HttpAttributes::kXForwardedFor, request_headers)) { |
| return true; |
| } |
| |
| return false; |
| } |
| |
| int64 RewriteOptions::ImageInlineMaxBytes() const { |
| if (Enabled(kInlineImages)) { |
| return image_inline_max_bytes_.value(); |
| } else { |
| return 0; |
| } |
| } |
| |
| void RewriteOptions::set_image_inline_max_bytes(int64 x) { |
| set_option(x, &image_inline_max_bytes_); |
| if (!css_image_inline_max_bytes_.was_set() && |
| x > css_image_inline_max_bytes_.value()) { |
| // Make sure css_image_inline_max_bytes is at least image_inline_max_bytes |
| // if it has not been explicitly configured. |
| css_image_inline_max_bytes_.set(x); |
| } |
| } |
| |
| int64 RewriteOptions::CssImageInlineMaxBytes() const { |
| if (Enabled(kInlineImages)) { |
| return css_image_inline_max_bytes_.value(); |
| } else { |
| return 0; |
| } |
| } |
| |
| int64 RewriteOptions::MaxImageInlineMaxBytes() const { |
| return std::max(ImageInlineMaxBytes(), |
| CssImageInlineMaxBytes()); |
| } |
| |
| void RewriteOptions::GetEnabledFiltersRequiringScriptExecution( |
| RewriteOptions::FilterVector* filters) const { |
| for (int i = 0, n = arraysize(kRequiresScriptExecutionFilterSet); i < n; |
| ++i) { |
| if (Enabled(kRequiresScriptExecutionFilterSet[i])) { |
| filters->push_back(kRequiresScriptExecutionFilterSet[i]); |
| } |
| } |
| } |
| |
| void RewriteOptions::DisableFiltersRequiringScriptExecution() { |
| for (int i = 0, n = arraysize(kRequiresScriptExecutionFilterSet); i < n; |
| ++i) { |
| DisableFilter(kRequiresScriptExecutionFilterSet[i]); |
| } |
| } |
| |
| bool RewriteOptions::UsePerOriginPropertyCachePage() const { |
| return Enabled(kMobilize); |
| } |
| |
| DomainLawyer* RewriteOptions::WriteableDomainLawyer() { |
| Modify(); |
| return domain_lawyer_.MakeWriteable(); |
| } |
| |
| JavascriptLibraryIdentification* RewriteOptions:: |
| WriteableJavascriptLibraryIdentification() { |
| Modify(); |
| return javascript_library_identification_.MakeWriteable(); |
| } |
| |
| void RewriteOptions::Merge(const RewriteOptions& src) { |
| DCHECK(!frozen_); |
| #ifndef NDEBUG |
| CHECK(src.MergeOK()); // DCHECK outside of the #ifndef does not link. |
| #endif |
| |
| bool modify = src.modified_; |
| |
| DCHECK_EQ(all_options_.size(), src.all_options_.size()); |
| DCHECK_EQ(initialized_options_, src.initialized_options_); |
| DCHECK_EQ(initialized_options_, all_options_.size()); |
| |
| // In the case of conflicts between extend_cache and preserve, remember |
| // which one should win before we merge the individual options and filters. |
| MergeOverride override_css = ComputeMergeOverride( |
| kExtendCacheCss, src.css_preserve_urls_, css_preserve_urls_, src); |
| MergeOverride override_images = ComputeMergeOverride( |
| kExtendCacheImages, src.image_preserve_urls_, image_preserve_urls_, src); |
| MergeOverride override_scripts = ComputeMergeOverride( |
| kExtendCacheScripts, src.js_preserve_urls_, js_preserve_urls_, src); |
| |
| // If this.forbid_all_disabled_filters() is true |
| // but src.forbid_all_disabled_filters() is false, |
| // the default merging logic will set it false in the result, but we need |
| // to toggle the value: once it's set it has to stay set. |
| bool new_forbid_all_disabled = (forbid_all_disabled_filters() || |
| src.forbid_all_disabled_filters()); |
| |
| // If ForbidAllDisabledFilters is turned on, it means no-one can enable a |
| // filter that isn't already enabled, meaning the filters enabled in 'src' |
| // cannot be enabled in 'this'. |
| if (!forbid_all_disabled_filters()) { |
| // Enabled filters in src override disabled filters in this. |
| disabled_filters_.EraseSet(src.enabled_filters_); |
| } |
| |
| modify |= enabled_filters_.Merge(src.enabled_filters_); |
| modify |= disabled_filters_.Merge(src.disabled_filters_); |
| |
| // Clean up enabled filters list to make debugging easier. |
| enabled_filters_.EraseSet(disabled_filters_); |
| |
| // Forbidden filters strictly merge, with no exclusions. E.g. You can never |
| // enable a filter in an .htaccess file that was forbidden above. |
| modify |= forbidden_filters_.Merge(src.forbidden_filters_); |
| |
| enabled_filters_.EraseSet(forbidden_filters_); |
| |
| for (FilterIdSet::const_iterator p = src.distributable_filters_.begin(), |
| e = src.distributable_filters_.end(); p != e; ++p) { |
| StringPiece filter_id = *p; |
| // Distributable filters union when merged. |
| distributable_filters_.insert(filter_id.as_string()); |
| } |
| |
| experiment_id_ = src.experiment_id_; |
| for (int i = 0, n = src.experiment_specs_.size(); i < n; ++i) { |
| ExperimentSpec* spec = src.experiment_specs_[i]->Clone(); |
| InsertExperimentSpecInVector(spec); |
| } |
| |
| if (src.downstream_cache_purge_location_prefix_.was_set()) { |
| set_downstream_cache_purge_location_prefix( |
| src.downstream_cache_purge_location_prefix()); |
| } |
| for (int i = 0, n = src.custom_fetch_headers_.size(); i < n; ++i) { |
| NameValue* nv = src.custom_fetch_headers_[i]; |
| AddCustomFetchHeader(nv->name, nv->value); |
| } |
| |
| for (int i = 0, n = src.num_url_valued_attributes(); i < n; ++i) { |
| StringPiece element; |
| StringPiece attribute; |
| semantic_type::Category category; |
| src.UrlValuedAttribute(i, &element, &attribute, &category); |
| AddUrlValuedAttribute(element, attribute, category); |
| } |
| |
| // Note that from the perspective of this class, we can be merging |
| // RewriteOptions subclasses & superclasses, so don't read anything |
| // that doesn't exist. However this is almost certainly the wrong |
| // thing to do -- we should ensure that within a system all the |
| // RewriteOptions that are instantiated are the same sublcass, so |
| // DCHECK that they have the same number of options. |
| DCHECK_EQ(all_options_.size(), src.all_options_.size()); |
| size_t options_to_merge = std::min(all_options_.size(), |
| src.all_options_.size()); |
| for (size_t i = 0; i < options_to_merge; ++i) { |
| all_options_[i]->Merge(src.all_options_[i]); |
| } |
| |
| FastWildcardGroupMap::const_iterator it = src.rejected_request_map_.begin(); |
| for (; it != src.rejected_request_map_.end(); ++it) { |
| std::pair<FastWildcardGroupMap::iterator, bool> insert_result = |
| rejected_request_map_.insert(std::make_pair( |
| it->first, static_cast<FastWildcardGroup*>(NULL))); |
| if (insert_result.second) { |
| insert_result.first->second = new FastWildcardGroup; |
| } |
| insert_result.first->second->AppendFrom(*it->second); |
| } |
| |
| domain_lawyer_.MergeOrShare(src.domain_lawyer_); |
| javascript_library_identification_.MergeOrShare( |
| src.javascript_library_identification_); |
| { |
| ScopedMutex this_lock(cache_purge_mutex_.get()); |
| ScopedMutex src_lock(src.cache_purge_mutex_.get()); |
| purge_set_.MergeOrShare(src.purge_set_); |
| } |
| |
| file_load_policy_.Merge(src.file_load_policy_); |
| allow_resources_.MergeOrShare(src.allow_resources_); |
| allow_when_inlining_resources_.MergeOrShare( |
| src.allow_when_inlining_resources_); |
| retain_comments_.MergeOrShare(src.retain_comments_); |
| lazyload_enabled_classes_.MergeOrShare(src.lazyload_enabled_classes_); |
| blocking_rewrite_referer_urls_.MergeOrShare( |
| src.blocking_rewrite_referer_urls_); |
| override_caching_wildcard_.MergeOrShare(src.override_caching_wildcard_); |
| |
| // Merge url_cache_invalidation_entries_ so that increasing order of timestamp |
| // is preserved (assuming this.url_cache_invalidation_entries_ and |
| // src.url_cache_invalidation_entries_ are both ordered). |
| int original_size = url_cache_invalidation_entries_.size(); |
| // Append copies of src's url cache invalidation entries to this. |
| for (int i = 0, n = src.url_cache_invalidation_entries_.size(); i < n; ++i) { |
| url_cache_invalidation_entries_.push_back( |
| src.url_cache_invalidation_entries_[i]->Clone()); |
| } |
| // Now url_cache_invalidation_entries_ consists of two ordered ranges: [begin, |
| // begin+original_size) and [begin+original_size, end). Hence we can use |
| // inplace_merge. |
| std::inplace_merge(url_cache_invalidation_entries_.begin(), |
| url_cache_invalidation_entries_.begin() + original_size, |
| url_cache_invalidation_entries_.end(), |
| RewriteOptions::CompareUrlCacheInvalidationEntry); |
| |
| // If either side has forbidden all disabled filters then the result must |
| // too. This is required to prevent subdirectories from turning it off when |
| // a parent directory has turned it on (by mod_instaweb.cc/merge_dir_config). |
| if (forbid_all_disabled_filters_.was_set() || |
| src.forbid_all_disabled_filters_.was_set()) { |
| set_forbid_all_disabled_filters(new_forbid_all_disabled); |
| } |
| |
| ApplyMergeOverride(override_css, kExtendCacheCss, &css_preserve_urls_); |
| ApplyMergeOverride(override_images, kExtendCacheImages, |
| &image_preserve_urls_); |
| ApplyMergeOverride(override_scripts, kExtendCacheScripts, &js_preserve_urls_); |
| |
| if (modify) { |
| Modify(); |
| } |
| } |
| |
| void RewriteOptions::MergeOnlyProcessScopeOptions(const RewriteOptions& src) { |
| DCHECK(!frozen_); |
| #ifndef NDEBUG // MergeOK is only around in CHECK-enabled builds. |
| CHECK(src.MergeOK()); |
| #endif |
| |
| DCHECK_EQ(all_options_.size(), src.all_options_.size()); |
| DCHECK_EQ(initialized_options_, src.initialized_options_); |
| DCHECK_EQ(initialized_options_, all_options_.size()); |
| |
| size_t options_to_merge = std::min(all_options_.size(), |
| src.all_options_.size()); |
| for (size_t i = 0; i < options_to_merge; ++i) { |
| OptionScope scope = all_options_[i]->scope(); |
| if (scope == kProcessScope || scope == kProcessScopeStrict) { |
| all_options_[i]->Merge(src.all_options_[i]); |
| } |
| } |
| |
| Modify(); |
| } |
| |
| RewriteOptions* RewriteOptions::Clone() const { |
| RewriteOptions* options = NewOptions(); |
| options->Merge(*this); |
| options->frozen_ = false; |
| options->modified_ = false; |
| return options; |
| } |
| |
| RewriteOptions* RewriteOptions::NewOptions() const { |
| return new RewriteOptions(thread_system_); |
| } |
| |
| GoogleString RewriteOptions::OptionSignature(const GoogleString& x, |
| const Hasher* hasher) { |
| return hasher->Hash(x); |
| } |
| |
| GoogleString RewriteOptions::OptionSignature(ResourceCategorySet x, |
| const Hasher* hasher) { |
| return hasher->Hash(ToString(x)); |
| } |
| |
| GoogleString RewriteOptions::OptionSignature(RewriteLevel level, |
| const Hasher* hasher) { |
| switch (level) { |
| case kPassThrough: return "p"; |
| case kCoreFilters: return "c"; |
| case kOptimizeForBandwidth: return "b"; |
| case kMobilizeFilters: return "m"; |
| case kTestingCoreFilters: return "t"; |
| case kAllFilters: return "a"; |
| } |
| return "?"; |
| } |
| |
| GoogleString RewriteOptions::OptionSignature(const BeaconUrl& beacon_url, |
| const Hasher* hasher) { |
| return hasher->Hash(ToString(beacon_url)); |
| } |
| |
| GoogleString RewriteOptions::OptionSignature(const MobTheme& theme, |
| const Hasher* hasher) { |
| GoogleString to_hash; |
| to_hash.push_back(theme.background_color.r); |
| to_hash.push_back(theme.background_color.g); |
| to_hash.push_back(theme.background_color.b); |
| to_hash.push_back(theme.foreground_color.r); |
| to_hash.push_back(theme.foreground_color.g); |
| to_hash.push_back(theme.foreground_color.b); |
| to_hash.append(theme.logo_url); |
| return hasher->Hash(to_hash); |
| } |
| |
| GoogleString RewriteOptions::OptionSignature( |
| const ResponsiveDensities& densities, const Hasher* hasher) { |
| return hasher->Hash(ToString(densities)); |
| } |
| |
| GoogleString RewriteOptions::OptionSignature( |
| const protobuf::MessageLite& proto, |
| const Hasher* hasher) { |
| return hasher->Hash(ToString(proto)); |
| } |
| |
| GoogleString RewriteOptions::OptionSignature( |
| const AllowVaryOn& allow_vary_on, const Hasher* hasher) { |
| GoogleString out; |
| char mask = |
| (allow_vary_on.allow_auto() | |
| (allow_vary_on.allow_accept() << 1) | |
| (allow_vary_on.allow_save_data() << 2) | |
| (allow_vary_on.allow_user_agent() << 3)); |
| Web64Encode(GoogleString(&mask, 1), &out); |
| return out; |
| } |
| |
| void RewriteOptions::DisableIfNotExplictlyEnabled(Filter filter) { |
| if (!enabled_filters_.IsSet(filter)) { |
| disabled_filters_.Insert(filter); |
| } |
| } |
| |
| RewriteOptions::MergeOverride RewriteOptions::ComputeMergeOverride( |
| Filter filter, const Option<bool>& src_preserve_option, |
| const Option<bool>& preserve_option, const RewriteOptions& src) { |
| |
| // Note: the order of the if and else-if matter. if both this and |
| // src have filter enabled and preserve_options set, then the filter |
| // would actually be disabled. |
| if (src.Enabled(filter) && preserve_option.value()) { |
| return kDisablePreserve; |
| } else if (Enabled(filter) && src_preserve_option.value()) { |
| return kDisableFilter; |
| } |
| return kNoAction; |
| } |
| |
| void RewriteOptions::ApplyMergeOverride(MergeOverride merge_override, |
| Filter filter, |
| Option<bool>* preserve_option) { |
| switch (merge_override) { |
| case kNoAction: |
| break; |
| case kDisablePreserve: |
| if (preserve_option->was_set()) { |
| preserve_option->set(false); |
| } |
| break; |
| case kDisableFilter: |
| enabled_filters_.Erase(filter); |
| disabled_filters_.Insert(filter); |
| break; |
| } |
| } |
| |
| void RewriteOptions::Freeze() { |
| if (!frozen_) { |
| frozen_ = true; |
| signature_.clear(); |
| } |
| } |
| |
| void RewriteOptions::ComputeSignature() { |
| ThreadSystem::ScopedReader read_lock(cache_purge_mutex_.get()); |
| ComputeSignatureLockHeld(); |
| } |
| |
| void RewriteOptions::ComputeSignatureLockHeld() { |
| if (frozen_) { |
| return; |
| } |
| #ifndef NDEBUG |
| if (!options_uniqueness_checked_) { |
| options_uniqueness_checked_ = true; |
| StringSet id_set; |
| for (int i = 0, n = all_options_.size(); i < n; ++i) { |
| const char* id = all_options_[i]->id(); |
| std::pair<StringSet::iterator, bool> insertion = id_set.insert(id); |
| DCHECK(insertion.second) << "Duplicate RewriteOption option id: " << id; |
| } |
| } |
| #endif |
| |
| signature_ = IntegerToString(kOptionsVersion); |
| for (int i = kFirstFilter; i != kEndOfFilters; ++i) { |
| Filter filter = static_cast<Filter>(i); |
| // Ignore the debug filter when computing signatures. Note that we still |
| // must have kDebug be considered in IsEqual though. |
| if ((filter != kDebug) && Enabled(filter)) { |
| StrAppend(&signature_, "_", FilterId(filter)); |
| } |
| } |
| signature_ += "O"; |
| for (int i = 0, n = all_options_.size(); i < n; ++i) { |
| // Keep the signature relatively short by only including options |
| // with values overridden from the default. |
| OptionBase* option = all_options_[i]; |
| if (option->is_used_for_signature_computation() && option->was_set()) { |
| StrAppend(&signature_, option->id(), ":", |
| option->Signature(hasher()), "_"); |
| } |
| } |
| if (javascript_library_identification() != NULL) { |
| StrAppend(&signature_, "LI:"); |
| javascript_library_identification()->AppendSignature(&signature_); |
| StrAppend(&signature_, "_"); |
| } |
| StrAppend(&signature_, domain_lawyer_->Signature(), "_"); |
| StrAppend(&signature_, "AR:", allow_resources_->Signature(), "_"); |
| StrAppend(&signature_, "AWIR:", |
| allow_when_inlining_resources_->Signature(), "_"); |
| StrAppend(&signature_, "RC:", retain_comments_->Signature(), "_"); |
| StrAppend(&signature_, "LDC:", lazyload_enabled_classes_->Signature(), "_"); |
| StrAppend(&signature_, "BRRU:", |
| blocking_rewrite_referer_urls_->Signature(), "_"); |
| StrAppend(&signature_, "UCI:"); |
| for (int i = 0, n = url_cache_invalidation_entries_.size(); i < n; ++i) { |
| const UrlCacheInvalidationEntry& entry = |
| *url_cache_invalidation_entries_[i]; |
| if (!entry.ignores_metadata_and_pcache) { |
| StrAppend(&signature_, entry.ComputeSignature(), "|"); |
| } |
| } |
| |
| // We do not include the PurgeSet signature, but that is included in |
| // RewriteOptions::IsEqual. |
| // |
| // TODO(jmarantz): Remove the global invalidation timestamp from the |
| // signature and add explicit timestamp checking where needed, such |
| // as pcache lookups. Note that it is already included in HTTPCache |
| // lookups. |
| StrAppend(&signature_, "GTS:", |
| Integer64ToString(purge_set_->global_invalidation_timestamp_ms()), |
| "_"); |
| |
| // rejected_request_map_ is not added to rewrite options signature as this |
| // should not affect rewriting and metadata or property cache lookups. |
| StrAppend(&signature_, "OC:", override_caching_wildcard_->Signature(), "_"); |
| |
| StrAppend(&signature_, SubclassSignatureLockHeld()); |
| |
| frozen_ = true; |
| |
| // TODO(jmarantz): Incorporate signature from file_load_policy. However, the |
| // changes made here make our system strictly more correct than it was before, |
| // using an ad-hoc signature in css_filter.cc. |
| } |
| |
| bool RewriteOptions::ClearSignatureWithCaution() { |
| bool recompute_signature = frozen_; |
| frozen_ = false; |
| #ifndef NDEBUG |
| last_thread_id_.reset(); |
| #endif |
| signature_.clear(); |
| return recompute_signature; |
| } |
| |
| bool RewriteOptions::IsEqual(const RewriteOptions& that) const { |
| DCHECK(frozen_); |
| DCHECK(that.frozen_); |
| if (signature() != that.signature()) { |
| return false; |
| } |
| |
| // kDebug is excluded from the signature but we better not exclude it |
| // from IsEqual. |
| if (Enabled(kDebug) != that.Enabled(kDebug)) { |
| return false; |
| } |
| |
| // TODO(jmarantz): move more stuff out of the signature() and into the |
| // IsEqual function. We might also want to make a second signature so |
| // that IsEqual is not too slow. |
| // |
| // TODO(jmarantz): consider making a second signature for the |
| // PurgeSet and other stuff that we exclude for |
| // the RewriteOptions::signature. |
| { |
| ThreadSystem::ScopedReader read_lock(cache_purge_mutex_.get()); |
| return purge_set_->Equals(*that.purge_set_); |
| } |
| } |
| |
| GoogleString RewriteOptions::ToString(const ResourceCategorySet &x) { |
| GoogleString result = ""; |
| const char* delim = ""; |
| for (ResourceCategorySet::const_iterator entry = x.begin(); |
| entry != x.end(); |
| ++entry) { |
| StrAppend(&result, delim, semantic_type::GetCategoryString(*entry)); |
| delim = ","; |
| } |
| return result; |
| } |
| |
| GoogleString RewriteOptions::ToString(RewriteLevel level) { |
| switch (level) { |
| case kPassThrough: return "Pass Through"; |
| case kOptimizeForBandwidth: return "Optimize For Bandwidth"; |
| case kCoreFilters: return "Core Filters"; |
| case kMobilizeFilters: return "Mobilize Filters"; |
| case kTestingCoreFilters: return "Testing Core Filters"; |
| case kAllFilters: return "All Filters"; |
| } |
| return "?"; |
| } |
| |
| GoogleString RewriteOptions::ToString(const BeaconUrl& beacon_url) { |
| GoogleString result = beacon_url.http; |
| if (beacon_url.http != beacon_url.https) { |
| StrAppend(&result, " ", beacon_url.https); |
| } |
| return result; |
| } |
| |
| GoogleString RewriteOptions::ToString(const MobTheme& theme) { |
| return StrCat(ToString(theme.background_color), " ", |
| ToString(theme.foreground_color), " ", |
| theme.logo_url); |
| } |
| |
| GoogleString RewriteOptions::ToString(const Color& color) { |
| return StringPrintf("#%02x%02x%02x", static_cast<int>(color.r), |
| static_cast<int>(color.g), static_cast<int>(color.b)); |
| } |
| |
| GoogleString RewriteOptions::ToString(const ResponsiveDensities& densities) { |
| GoogleString result = ""; |
| const char* delim = ""; |
| for (ResponsiveDensities::const_iterator iter = densities.begin(); |
| iter != densities.end(); ++iter) { |
| // 4 digits of precision seems like plenty. Note: hashing doubles is |
| // generally not a good idea. But in this case we are only parsing the |
| // doubles from config and never performing any arithmetic, so hashing |
| // should be alright. |
| StrAppend(&result, delim, StringPrintf("%.4g", *iter)); |
| delim = ","; |
| } |
| return result; |
| } |
| |
| GoogleString RewriteOptions::ToString(const protobuf::MessageLite& proto) { |
| return proto.SerializeAsString(); |
| } |
| |
| GoogleString RewriteOptions::FilterSetToString( |
| const FilterSet& filter_set) const { |
| GoogleString output; |
| for (int i = kFirstFilter; i != kEndOfFilters; ++i) { |
| Filter filter = static_cast<Filter>(i); |
| if (filter_set.IsSet(filter)) { |
| StrAppend(&output, FilterId(filter), "\t", FilterName(filter), "\n"); |
| } |
| } |
| return output; |
| } |
| |
| GoogleString RewriteOptions::AllowVaryOn::ToString() const { |
| GoogleString result = ""; |
| const char* delim = ""; |
| if (allow_auto()) { |
| result = kAutoString; |
| // Make sure all other options have been set correctly. |
| DCHECK(!allow_accept()); |
| DCHECK(!allow_user_agent()); |
| DCHECK(allow_save_data()); |
| } else { |
| if (allow_accept()) { |
| StrAppend(&result, delim, HttpAttributes::kAccept); |
| delim = ","; |
| } |
| if (allow_save_data()) { |
| StrAppend(&result, delim, HttpAttributes::kSaveData); |
| delim = ","; |
| } |
| if (allow_user_agent()) { |
| StrAppend(&result, delim, HttpAttributes::kUserAgent); |
| } |
| if (result.empty()) { |
| result = kNoneString; |
| } |
| } |
| return result; |
| } |
| |
| GoogleString RewriteOptions::ToString(const AllowVaryOn& allow_vary_on) { |
| return allow_vary_on.ToString(); |
| } |
| |
| GoogleString RewriteOptions::EnabledFiltersToString() const { |
| GoogleString output; |
| for (int i = kFirstFilter; i != kEndOfFilters; ++i) { |
| Filter filter = static_cast<Filter>(i); |
| if (Enabled(filter)) { |
| StrAppend(&output, FilterId(filter), "\t", FilterName(filter), "\n"); |
| } |
| } |
| return output; |
| } |
| |
| GoogleString RewriteOptions::SafeEnabledOptionsToString() const { |
| GoogleString output; |
| for (int i = 0, n = all_options_.size(); i < n; ++i) { |
| OptionBase* option = all_options_[i]; |
| if (option->was_set() && option->property()->safe_to_print()) { |
| GoogleString name_and_id = |
| StrCat(option->option_name(), " (", option->id(), ") "); |
| StrAppend(&output, name_and_id, option->ToString(), "\n"); |
| } |
| } |
| return output; |
| } |
| |
| GoogleString RewriteOptions::OptionsToString() const { |
| GoogleString output; |
| StrAppend(&output, "Version: ", IntegerToString(kOptionsVersion), ": "); |
| |
| switch (enabled_.value()) { |
| case kEnabledOff: StrAppend(&output, "off\n\n"); break; |
| case kEnabledOn: StrAppend(&output, "on\n\n"); break; |
| case kEnabledUnplugged: StrAppend(&output, "unplugged\n\n"); break; |
| } |
| output += "Filters\n"; |
| for (int i = kFirstFilter; i != kEndOfFilters; ++i) { |
| Filter filter = static_cast<Filter>(i); |
| if (Enabled(filter)) { |
| StrAppend(&output, FilterId(filter), "\t", FilterName(filter), "\n"); |
| } |
| } |
| |
| // Print the options. Use two passes so we can line up the values, given that |
| // the names have different widths. |
| output += "\nOptions\n"; |
| StringVector names, values; |
| int max_width = 0; |
| for (int i = 0, n = all_options_.size(); i < n; ++i) { |
| // Only including options with values overridden from the default. |
| OptionBase* option = all_options_[i]; |
| if (option->was_set()) { |
| GoogleString name_and_id = StrCat(option->option_name(), |
| " (", option->id(), ")"); |
| max_width = std::max(max_width, static_cast<int>(name_and_id.size())); |
| names.push_back(name_and_id); |
| values.push_back(option->ToString()); |
| } |
| } |
| for (int i = 0, n = values.size(); i < n; ++i) { |
| GoogleString spaces(max_width - names[i].size() + 2, ' '); |
| StrAppend(&output, " ", names[i], spaces, values[i], "\n"); |
| } |
| |
| output += "\nDomain Lawyer\n"; |
| StrAppend(&output, domain_lawyer_->ToString(" ")); |
| // TODO(mmohabey): Incorporate ToString() from the file_load_policy, |
| // allow_resources, and retain_comments. |
| |
| if (!url_cache_invalidation_entries_.empty()) { |
| StrAppend(&output, "\nURL cache invalidation entries\n"); |
| for (int i = 0, n = url_cache_invalidation_entries_.size(); i < n; ++i) { |
| StrAppend(&output, " ", url_cache_invalidation_entries_[i]->ToString(), |
| "\n"); |
| } |
| } |
| |
| if (rejected_request_map_.size() > 0) { |
| StrAppend(&output, "\nRejected request map\n"); |
| FastWildcardGroupMap::const_iterator it = rejected_request_map_.begin(); |
| for (; it != rejected_request_map_.end(); ++it) { |
| StrAppend(&output, " ", it->first, " ", it->second->Signature(), "\n"); |
| } |
| } |
| GoogleString override_caching_wildcard_string( |
| override_caching_wildcard_->Signature()); |
| if (!override_caching_wildcard_string.empty()) { |
| StrAppend(&output, "\nOverride caching wildcards\n", |
| override_caching_wildcard_string); |
| } |
| |
| for (int i = 0, n = experiment_specs_.size(); i < n; ++i) { |
| RewriteOptions::ExperimentSpec* spec = experiment_specs_[i]; |
| StrAppend(&output, "Experiment ", spec->ToString(), "\n"); |
| } |
| |
| { |
| ThreadSystem::ScopedReader read_lock(cache_purge_mutex_.get()); |
| if (has_cache_invalidation_timestamp_ms()) { |
| int64 cache_invalidation_ms = cache_invalidation_timestamp(); |
| GoogleString time_string; |
| if ((cache_invalidation_ms > 0) && |
| ConvertTimeToString(cache_invalidation_ms, &time_string)) { |
| StrAppend(&output, "\nInvalidation Timestamp: ", |
| time_string, " (", Integer64ToString(cache_invalidation_ms), |
| ")\n"); |
| } |
| } else { |
| StrAppend(&output, "\nInvalidation Timestamp: (none)"); |
| } |
| } |
| |
| return output; |
| } |
| |
| GoogleString RewriteOptions::ExperimentSpec::QuoteHostPort( |
| const GoogleString& in) { |
| if (in.find(":") != GoogleString::npos) { |
| return StrCat("\"", in, "\""); |
| } |
| return in; |
| } |
| |
| GoogleString RewriteOptions::ExperimentSpec::ToString() const { |
| GoogleString out; |
| StrAppend(&out, "id=", IntegerToString(id_)); |
| if (ga_variable_slot_ != kDefaultExperimentSlot) { |
| StrAppend(&out, "slot=", IntegerToString(ga_variable_slot_)); |
| } |
| if (!ga_id_.empty()) { |
| StrAppend(&out, ";ga=", ga_id_); |
| } |
| StrAppend(&out, ";percent=", IntegerToString(percent_)); |
| if (rewrite_level_ != kPassThrough) { |
| StrAppend(&out, ";level=", RewriteOptions::ToString(rewrite_level_)); |
| } |
| |
| if (use_default_) { |
| StrAppend(&out, ";default"); |
| } |
| |
| // TODO(jefftk): Put these in the form "rewrite_images" instead of "ri". |
| const char* sep = ";enabled="; |
| for (int i = kFirstFilter; i != kEndOfFilters; ++i) { |
| Filter filter = static_cast<Filter>(i); |
| if (enabled_filters_.IsSet(filter)) { |
| StrAppend(&out, sep, FilterId(filter)); |
| sep = ","; |
| } |
| } |
| |
| sep = ";disabled="; |
| for (int i = kFirstFilter; i != kEndOfFilters; ++i) { |
| Filter filter = static_cast<Filter>(i); |
| if (disabled_filters_.IsSet(filter)) { |
| StrAppend(&out, sep, FilterId(filter)); |
| sep = ","; |
| } |
| } |
| |
| sep = ";options="; |
| for (RewriteOptions::OptionSet::const_iterator p = filter_options_.begin(), |
| e = filter_options_.end(); p != e; ++p) { |
| StrAppend(&out, sep, p->first, "=", p->second); |
| sep = ","; |
| } |
| |
| if (matches_device_types_.get() != NULL) { |
| StrAppend(&out, ";matches_device_type="); |
| sep = ""; |
| if ((*matches_device_types_)[UserAgentMatcher::kDesktop]) { |
| StrAppend(&out, sep, "desktop"); |
| sep = ","; |
| } |
| if ((*matches_device_types_)[UserAgentMatcher::kTablet]) { |
| StrAppend(&out, sep, "tablet"); |
| sep = ","; |
| } |
| if ((*matches_device_types_)[UserAgentMatcher::kMobile]) { |
| StrAppend(&out, sep, "mobile"); |
| sep = ","; |
| } |
| } |
| |
| for (AlternateOriginDomains::const_iterator i = |
| alternate_origin_domains_.begin(); |
| i != alternate_origin_domains_.end(); ++i) { |
| const AlternateOriginDomainSpec& spec = *i; |
| |
| StringVector quoted_serving_domains = spec.serving_domains; |
| for (StringVector::iterator i = quoted_serving_domains.begin(); |
| i != quoted_serving_domains.end(); ++i) { |
| *i = QuoteHostPort(*i); |
| } |
| |
| StrAppend(&out, ";alternate_origin_domain=", |
| JoinCollection(quoted_serving_domains, ","), ":", |
| QuoteHostPort(spec.origin_domain)); |
| |
| if (!spec.host_header.empty()) { |
| StrAppend(&out, ":", QuoteHostPort(spec.host_header)); |
| } |
| } |
| |
| return out; |
| } |
| |
| GoogleString RewriteOptions::ToExperimentString() const { |
| // Only add the experiment id if we're running this experiment. |
| if (GetExperimentSpec(experiment_id_) != NULL) { |
| return StringPrintf("Experiment: %d", experiment_id_); |
| } |
| return GoogleString(); |
| } |
| |
| GoogleString RewriteOptions::ToExperimentDebugString() const { |
| GoogleString output = ToExperimentString(); |
| if (!output.empty()) { |
| output += "; "; |
| } |
| if (!running_experiment()) { |
| output += "off; "; |
| } else if (experiment_id_ == experiment::kExperimentNotSet) { |
| output += "not set; "; |
| } else if (experiment_id_ == experiment::kNoExperiment) { |
| output += "no experiment; "; |
| } else { |
| ExperimentSpec* spec = GetExperimentSpec(experiment_id_); |
| if (spec != NULL) { |
| output += spec->ToString(); |
| } |
| } |
| return output; |
| } |
| |
| void RewriteOptions::Modify() { |
| DCHECK(!frozen_); |
| modified_ = true; |
| |
| // The data in last_thread_id_ is currently only examined in DCHECKs so |
| // there's no need to pay the cost of populating it in production. |
| #ifndef NDEBUG |
| if (thread_system_ != NULL) { |
| if (last_thread_id_.get() == NULL) { |
| last_thread_id_.reset(thread_system_->GetThreadId()); |
| } else { |
| DCHECK(ModificationOK()); |
| } |
| } |
| #endif |
| } |
| |
| // These method implementations are only in debug builds for asserting that |
| // the usage patterns are safe. In fact we don't even have last_thread_id_ |
| // compiled into the class in non-debug compiles. |
| #ifndef NDEBUG |
| bool RewriteOptions::ModificationOK() const { |
| return ((last_thread_id_.get() == NULL) || |
| (last_thread_id_->IsCurrentThread())); |
| } |
| |
| bool RewriteOptions::MergeOK() const { |
| return frozen_ || (last_thread_id_.get() == NULL) || |
| last_thread_id_->IsCurrentThread(); |
| } |
| #endif |
| |
| void RewriteOptions::AddCustomFetchHeader(const StringPiece& name, |
| const StringPiece& value) { |
| custom_fetch_headers_.push_back(new NameValue(name, value)); |
| } |
| |
| // We expect experiment_specs_.size() to be small (not more than 2 or 3) |
| // so there is no need to optimize this. |
| RewriteOptions::ExperimentSpec* RewriteOptions::GetExperimentSpec( |
| int id) const { |
| for (int i = 0, n = experiment_specs_.size(); i < n; ++i) { |
| if (experiment_specs_[i]->id() == id) { |
| return experiment_specs_[i]; |
| } |
| } |
| return NULL; |
| } |
| |
| bool RewriteOptions::AvailableExperimentId(int id) { |
| if (id < 0 || id == experiment::kExperimentNotSet || |
| id == experiment::kNoExperiment) { |
| return false; |
| } |
| return (GetExperimentSpec(id) == NULL); |
| } |
| |
| RewriteOptions::ExperimentSpec* RewriteOptions::AddExperimentSpec( |
| const StringPiece& spec, MessageHandler* handler) { |
| ExperimentSpec* f_spec = new ExperimentSpec(spec, this, handler); |
| if (!InsertExperimentSpecInVector(f_spec)) { |
| return NULL; // InsertExperimentSpecInVector deletes f_spec on failure. |
| } |
| return f_spec; |
| } |
| |
| bool RewriteOptions::InsertExperimentSpecInVector(ExperimentSpec* spec) { |
| // See RewriteOptions::GetExperimentStateStr for why we can't have more than |
| // 26. |
| if (!AvailableExperimentId(spec->id()) || spec->percent() < 0 || |
| experiment_percent_ + spec->percent() > 100 || |
| experiment_specs_.size() + 1 > 26) { |
| delete spec; |
| return false; |
| } |
| experiment_specs_.push_back(spec); |
| experiment_percent_ += spec->percent(); |
| return true; |
| } |
| |
| // Always enable add_head, insert_ga, add_instrumentation, and HtmlWriter. This |
| // is considered a "no-filter" base for experiments. |
| // Note: insert_ga no longer needs add_head, but add_instrumentation still does. |
| bool RewriteOptions::SetupExperimentRewriters() { |
| // Don't change anything if we're not in an experiment or have some |
| // unset id. |
| if (experiment_id_ == experiment::kExperimentNotSet || |
| experiment_id_ == experiment::kNoExperiment) { |
| return true; |
| } |
| // Control: just make sure that the necessary stuff is on. |
| // Do NOT try to set up things to look like the ExperimentSpec |
| // for this id: it doesn't match the rewrite options. |
| ExperimentSpec* spec = GetExperimentSpec(experiment_id_); |
| if (spec == NULL) { |
| return false; |
| } |
| |
| if (!spec->ga_id().empty()) { |
| set_ga_id(spec->ga_id()); |
| } |
| |
| set_experiment_ga_slot(spec->slot()); |
| |
| // 'default' means keep the current filters, otherwise clear them -and- set |
| // the level. Note that we cannot set the level if 'default' is on because |
| // the default level is PassThrough which breaks the idea of 'default'. |
| if (!spec->use_default()) { |
| ClearFilters(); |
| SetRewriteLevel(spec->rewrite_level()); |
| } |
| EnableFilters(spec->enabled_filters()); |
| DisableFilters(spec->disabled_filters()); |
| // spec doesn't specify forbidden filters so no need to call ForbidFilters(). |
| // We need these for the experiment to work properly. |
| SetRequiredExperimentFilters(); |
| // Options were already checked during config parsing. |
| NullMessageHandler null_message_handler; |
| SetOptionsFromName(spec->filter_options(), &null_message_handler); |
| spec->ApplyAlternateOriginsToDomainLawyer(WriteableDomainLawyer(), |
| &null_message_handler); |
| return true; |
| } |
| |
| void RewriteOptions::SetRequiredExperimentFilters() { |
| ForceEnableFilter(RewriteOptions::kAddHead); |
| ForceEnableFilter(RewriteOptions::kAddInstrumentation); |
| ForceEnableFilter(RewriteOptions::kComputeStatistics); |
| ForceEnableFilter(RewriteOptions::kInsertGA); |
| ForceEnableFilter(RewriteOptions::kHtmlWriterFilter); |
| } |
| |
| RewriteOptions::ExperimentSpec::ExperimentSpec(const StringPiece& spec, |
| const RewriteOptions* options, |
| MessageHandler* handler) |
| : id_(experiment::kExperimentNotSet), |
| ga_id_(options->ga_id()), |
| ga_variable_slot_(options->experiment_ga_slot()), |
| percent_(-1), |
| rewrite_level_(kPassThrough), |
| use_default_(false) { |
| Initialize(spec, handler); |
| } |
| |
| RewriteOptions::ExperimentSpec::ExperimentSpec(int id) |
| : id_(id), |
| ga_id_(""), |
| ga_variable_slot_(kDefaultExperimentSlot), |
| percent_(-1), |
| rewrite_level_(kPassThrough), |
| use_default_(false) { |
| } |
| |
| RewriteOptions::ExperimentSpec::~ExperimentSpec() { } |
| |
| void RewriteOptions::ExperimentSpec::Merge(const ExperimentSpec& spec) { |
| enabled_filters_.Merge(spec.enabled_filters_); |
| disabled_filters_.Merge(spec.disabled_filters_); |
| for (OptionSet::const_iterator iter = spec.filter_options_.begin(); |
| iter != spec.filter_options_.end(); ++iter) { |
| filter_options_.insert(*iter); |
| } |
| ga_id_ = spec.ga_id_; |
| ga_variable_slot_ = spec.ga_variable_slot_; |
| percent_ = spec.percent_; |
| rewrite_level_ = spec.rewrite_level_; |
| use_default_ = spec.use_default_; |
| if (spec.matches_device_types_.get() != NULL) { |
| matches_device_types_.reset( |
| new DeviceTypeBitSet(*spec.matches_device_types_)); |
| } |
| if (!spec.alternate_origin_domains_.empty()) { |
| alternate_origin_domains_ = spec.alternate_origin_domains_; |
| } |
| } |
| |
| RewriteOptions::ExperimentSpec* RewriteOptions::ExperimentSpec::Clone() { |
| ExperimentSpec* ret = new ExperimentSpec(id_); |
| ret->Merge(*this); |
| return ret; |
| } |
| |
| // Options are written in the form: |
| // ExperimentSpec 'id= 2; percent= 20; RewriteLevel= CoreFilters; |
| // enable= resize_images; disable = is; inline_css = 25556; ga=UA-233842-1' |
| void RewriteOptions::ExperimentSpec::Initialize(const StringPiece& spec, |
| MessageHandler* handler) { |
| StringPieceVector spec_pieces; |
| SplitStringPieceToVector(spec, ";", &spec_pieces, true); |
| for (int i = 0, n = spec_pieces.size(); i < n; ++i) { |
| StringPiece piece = spec_pieces[i]; |
| TrimWhitespace(&piece); |
| if (StringCaseStartsWith(piece, "id")) { |
| StringPiece id = PieceAfterEquals(piece); |
| if (id.length() > 0 && !StringToInt(id, &id_)) { |
| // If we failed to turn this string into an int, then |
| // set the id_ to kExperimentNotSet so we don't end up adding |
| // in this spec. |
| id_ = experiment::kExperimentNotSet; |
| } |
| } else if (StringCaseEqual(piece, "default")) { |
| // "Default" means use whatever RewriteOptions are. |
| use_default_ = true; |
| } else if (StringCaseStartsWith(piece, "percent")) { |
| StringPiece percent = PieceAfterEquals(piece); |
| StringToInt(percent, &percent_); |
| } else if (StringCaseStartsWith(piece, "ga")) { |
| StringPiece ga = PieceAfterEquals(piece); |
| if (ga.length() > 0) { |
| ga_id_ = GoogleString(ga.data(), ga.length()); |
| } |
| } else if (StringCaseStartsWith(piece, "slot")) { |
| StringPiece slot = PieceAfterEquals(piece); |
| int stored_id = ga_variable_slot_; |
| StringToInt(slot, &ga_variable_slot_); |
| // Valid custom variable slots are 1-5 inclusive. |
| if (ga_variable_slot_ < 1 || ga_variable_slot_ > 5) { |
| LOG(INFO) << "Invalid custom variable slot."; |
| ga_variable_slot_ = stored_id; |
| } |
| } else if (StringCaseStartsWith(piece, "level")) { |
| StringPiece level = PieceAfterEquals(piece); |
| if (level.length() > 0) { |
| ParseRewriteLevel(level, &rewrite_level_); |
| } |
| } else if (StringCaseStartsWith(piece, "enable")) { |
| StringPiece enabled = PieceAfterEquals(piece); |
| if (enabled.length() > 0) { |
| AddCommaSeparatedListToFilterSet(enabled, &enabled_filters_, handler); |
| } |
| } else if (StringCaseStartsWith(piece, "disable")) { |
| StringPiece disabled = PieceAfterEquals(piece); |
| if (disabled.length() > 0) { |
| AddCommaSeparatedListToFilterSet(disabled, &disabled_filters_, handler); |
| } |
| } else if (StringCaseStartsWith(piece, "options")) { |
| StringPiece options = PieceAfterEquals(piece); |
| if (options.length() > 0) { |
| AddCommaSeparatedListToOptionSet(options, &filter_options_, handler); |
| } |
| } else if (StringCaseStartsWith(piece, "matches_device_type")) { |
| matches_device_types_.reset(new DeviceTypeBitSet()); |
| ParseDeviceTypeBitSet(PieceAfterEquals(piece), |
| matches_device_types_.get(), handler); |
| } else if (StringCaseStartsWith(piece, "alternate_origin_domain=")) { |
| alternate_origin_domains_.push_back(AlternateOriginDomainSpec()); |
| if (!ParseAlternateOriginDomain(PieceAfterEquals(piece), |
| &alternate_origin_domains_.back(), |
| handler)) { |
| handler->Message(kWarning, |
| "Ignorning invalid alternate_origin_domain: '%s'", |
| piece.as_string().c_str()); |
| alternate_origin_domains_.pop_back(); |
| } |
| } else { |
| handler->Message(kWarning, "Skipping unknown experiment setting: %s", |
| piece.as_string().c_str()); |
| } |
| } |
| } |
| |
| void RewriteOptions::ExperimentSpec::CombineQuotedHostPort( |
| StringPieceVector* vec, size_t first_pos, |
| GoogleString* combined_container) { |
| if (first_pos + 1 >= vec->size()) { |
| return; |
| } |
| |
| StringPiece& a = (*vec)[first_pos]; |
| StringPiece& b = (*vec)[first_pos + 1]; |
| |
| if (a.starts_with("\"") && b.ends_with("\"")) { |
| a.remove_prefix(1); |
| b.remove_suffix(1); |
| *combined_container = a.as_string() + ":" + b.as_string(); |
| (*vec)[first_pos] = StringPiece(*combined_container); |
| vec->erase(vec->begin() + first_pos + 1); |
| } |
| } |
| |
| bool RewriteOptions::ExperimentSpec::LooksLikeValidHost( |
| const StringPiece& host_str) { |
| StringPieceVector host_components; |
| SplitStringPieceToVector(host_str, ":", &host_components, false); |
| |
| if (host_components.empty() || host_components.size() > 2) { |
| return false; |
| } |
| |
| // host_components[0] is the host component. Just check it contains |
| // a non-numeric, so we know it's not empty or a stray port number. |
| if (host_components[0].find_first_not_of("1234567890") == StringPiece::npos) { |
| return false; |
| } |
| |
| // host_components[1] is the port, which may not be present. Check it is |
| // non-empty and contains only numbers. |
| if (host_components.size() > 1 && (host_components[1].empty() || |
| host_components[1].find_first_not_of( |
| "1234567890") != GoogleString::npos)) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool RewriteOptions::ExperimentSpec::ParseAlternateOriginDomain( |
| const StringPiece& in, AlternateOriginDomainSpec* out, |
| MessageHandler* handler) { |
| // Input format: serving_domain[,...]:alt_origin_domain[:host_header] |
| // alt_origin_domain and host_header can include a port, in which case |
| // they must be quoted: |
| // serving_domain:"alt_origin_domain:port":"host_header:port". |
| // A *single* serving_domain may have a port added in the same fashion. |
| |
| StringPieceVector args_str; |
| SplitStringPieceToVector(in, ":", &args_str, false); |
| |
| // serving_domain can be a comma separated list, however this code that deals |
| // with unescaping only allows a single quoted host:port in that field. Fixing |
| // that properly is too much work for a feature only required by tests. |
| GoogleString serving_combined_container; |
| if (args_str.size() >= 2) { |
| CombineQuotedHostPort(&args_str, 0, &serving_combined_container); |
| } |
| |
| GoogleString ref_combined_container; |
| if (args_str.size() >= 3) { |
| CombineQuotedHostPort(&args_str, 1, &ref_combined_container); |
| } |
| |
| GoogleString host_combined_container; |
| if (args_str.size() >= 4) { |
| CombineQuotedHostPort(&args_str, 2, &host_combined_container); |
| } |
| |
| if (args_str.size() < 2 || args_str.size() > 3) { |
| handler->Message( |
| kWarning, "Incorrect number of arguments for alternate_origin_domain"); |
| return false; |
| } |
| |
| out->serving_domains.clear(); |
| out->origin_domain = args_str[1].as_string(); |
| if (args_str.size() > 2) { |
| out->host_header = args_str[2].as_string(); |
| } else { |
| out->host_header.clear(); |
| } |
| |
| // We now attempt to configure a DomainLaywer with the supplied arguments. |
| // If there is a problem, we want to find out now (ie: parse time) and |
| // not when we later try and configure a DomainLawyer for real. |
| // We also check for non-numeric in the headers, since that's likely a stray |
| // port number and a valid hostname must contain a non-numeric. |
| |
| DomainLawyer lawyer; |
| // origin_domain cannot be empty or the lawyer will be very unhappy. |
| if (!LooksLikeValidHost(out->origin_domain) || |
| !lawyer.AddTwoProtocolOriginDomainMapping(out->origin_domain, "good.com", |
| "", handler)) { |
| handler->Message(kWarning, "Invalid origin domain: '%s'", |
| out->origin_domain.c_str()); |
| // This breaks *everything* else below, so we have to early exit. |
| return false; |
| } |
| |
| lawyer.Clear(); |
| if (!out->host_header.empty() && |
| (!LooksLikeValidHost(out->host_header) || |
| !lawyer.AddTwoProtocolOriginDomainMapping(out->origin_domain, "good.com", |
| out->host_header, handler))) { |
| handler->Message(kWarning, "Invalid host header: '%s'", |
| out->host_header.c_str()); |
| return false; |
| } |
| |
| StringPieceVector serving_domains; |
| SplitStringPieceToVector(args_str[0], ",", &serving_domains, true); |
| |
| lawyer.Clear(); |
| |
| for (StringPieceVector::const_iterator i = serving_domains.begin(); |
| i != serving_domains.end(); ++i) { |
| const StringPiece& serving_domain = *i; |
| if (LooksLikeValidHost(serving_domain) && |
| lawyer.AddTwoProtocolOriginDomainMapping( |
| out->origin_domain, serving_domain, out->host_header, handler)) { |
| out->serving_domains.push_back(serving_domain.as_string()); |
| } else { |
| handler->Message(kWarning, "Invalid serving domain: '%s'", |
| serving_domain.as_string().c_str()); |
| } |
| } |
| |
| return !out->serving_domains.empty(); |
| } |
| |
| bool RewriteOptions::ExperimentSpec::ParseDeviceTypeBitSet( |
| const StringPiece& in, ExperimentSpec::DeviceTypeBitSet* out, |
| MessageHandler* handler) { |
| bool success = false; |
| |
| StringPieceVector devices; |
| SplitStringPieceToVector(in, ",", &devices, true); |
| |
| for (int i = 0, n = devices.size(); i < n; ++i) { |
| StringPiece device = devices[i]; |
| |
| UserAgentMatcher::DeviceType device_type = |
| UserAgentMatcher::kEndOfDeviceType; |
| |
| if (device == "desktop") { |
| device_type = UserAgentMatcher::kDesktop; |
| } else if (device == "mobile") { |
| device_type = UserAgentMatcher::kMobile; |
| } else if (device == "tablet") { |
| device_type = UserAgentMatcher::kTablet; |
| } |
| |
| if (device_type != UserAgentMatcher::kEndOfDeviceType) { |
| out->set(device_type, true); |
| success = true; |
| } else { |
| handler->Message(kWarning, "Skipping unknown device type: %s", |
| device.as_string().c_str()); |
| } |
| } |
| |
| return success; |
| } |
| |
| bool RewriteOptions::ExperimentSpec::matches_device_type( |
| UserAgentMatcher::DeviceType type) const { |
| |
| // It would be nice to use matches_device_types_->size() for the second |
| // if clause. Unfortunately, matches_device_types_ might be NULL and |
| // size is not static, despite it being a template paramater. |
| if (type < 0 || type >= UserAgentMatcher::kEndOfDeviceType) { |
| LOG(DFATAL) << "DeviceType out of range: " << type; |
| return false; |
| } |
| |
| // If no device_type filter has been specified, this will match all devices. |
| if (matches_device_types_.get() == NULL) { |
| return true; |
| } |
| |
| return (*matches_device_types_)[type]; |
| } |
| |
| void RewriteOptions::ExperimentSpec::ApplyAlternateOriginsToDomainLawyer( |
| DomainLawyer* lawyer, MessageHandler* handler) const { |
| for (AlternateOriginDomains::const_iterator i = |
| alternate_origin_domains_.begin(); |
| i != alternate_origin_domains_.end(); ++i) { |
| const AlternateOriginDomainSpec& alt_spec = *i; |
| |
| for (StringVector::const_iterator j = alt_spec.serving_domains.begin(); |
| j != alt_spec.serving_domains.end(); ++j) { |
| const GoogleString& serving_domain = *j; |
| |
| lawyer->AddTwoProtocolOriginDomainMapping(alt_spec.origin_domain, |
| serving_domain, |
| alt_spec.host_header, handler); |
| } |
| } |
| } |
| |
| void RewriteOptions::AddInlineUnauthorizedResourceType( |
| semantic_type::Category category) { |
| inline_unauthorized_resource_types_.mutable_value().insert(category); |
| } |
| |
| bool RewriteOptions::HasInlineUnauthorizedResourceType( |
| semantic_type::Category category) const { |
| return inline_unauthorized_resource_types_.value().find(category) != |
| inline_unauthorized_resource_types_.value().end(); |
| } |
| |
| void RewriteOptions::ClearInlineUnauthorizedResourceTypes() { |
| inline_unauthorized_resource_types_.mutable_value().clear(); |
| } |
| |
| void RewriteOptions::set_inline_unauthorized_resource_types( |
| ResourceCategorySet x) { |
| set_option(x, &inline_unauthorized_resource_types_); |
| } |
| |
| void RewriteOptions::AddUrlValuedAttribute( |
| const StringPiece& element, const StringPiece& attribute, |
| semantic_type::Category category) { |
| if (url_valued_attributes_ == NULL) { |
| url_valued_attributes_.reset(new std::vector<ElementAttributeCategory>()); |
| } |
| ElementAttributeCategory eac; |
| element.CopyToString(&eac.element); |
| attribute.CopyToString(&eac.attribute); |
| eac.category = category; |
| url_valued_attributes_->push_back(eac); |
| } |
| |
| void RewriteOptions::UrlValuedAttribute( |
| int index, StringPiece* element, StringPiece* attribute, |
| semantic_type::Category* category) const { |
| const ElementAttributeCategory& eac = (*url_valued_attributes_)[index]; |
| *element = StringPiece(eac.element); |
| *attribute = StringPiece(eac.attribute); |
| *category = eac.category; |
| } |
| |
| bool RewriteOptions::IsUrlCacheValid(StringPiece url, int64 time_ms, |
| bool search_wildcards) const { |
| { |
| ThreadSystem::ScopedReader read_lock(cache_purge_mutex_.get()); |
| if (!purge_set_->IsValid(url.as_string(), time_ms)) { |
| return false; |
| } |
| } |
| |
| if (!search_wildcards) { |
| return true; |
| } |
| |
| // Check legacy wildcards. Hopefully there aren't any or this may be |
| // quite slow. |
| int i = 0; |
| int n = url_cache_invalidation_entries_.size(); |
| while (i < n && time_ms > url_cache_invalidation_entries_[i]->timestamp_ms) { |
| ++i; |
| } |
| // Now all entries from 0 to i-1 have timestamp less than time_ms and hence |
| // cannot invalidate a url cached at time_ms. |
| // TODO(sriharis): Should we use binary search instead of the above loop? |
| // Probably does not make sense as long as the following while loop is there. |
| |
| // Once FastWildcardGroup is in, we should check if it makes sense to make a |
| // FastWildcardGroup of Wildcards from position i to n-1, and Match against |
| // it. |
| while (i < n) { |
| if (url_cache_invalidation_entries_[i]->url_pattern.Match(url)) { |
| return false; |
| } |
| ++i; |
| } |
| return true; |
| } |
| |
| void RewriteOptions::PurgeUrl(StringPiece url, int64 timestamp_ms) { |
| ScopedMutex lock(cache_purge_mutex_.get()); |
| // Note that in this API, we do not handle failure due to moving |
| // backwards in time. This API is used for collecting purge-records |
| // from a database, and not for handling PURGE http requests. That |
| // is handled in ../apache/instaweb_handler.cc, handle_purge_request(). |
| purge_set_.MakeWriteable()->Put(url.as_string(), timestamp_ms); |
| } |
| |
| void RewriteOptions::AddUrlCacheInvalidationEntry( |
| StringPiece url_pattern, int64 timestamp_ms, |
| bool ignores_metadata_and_pcache) { |
| if (enable_cache_purge() && |
| !ignores_metadata_and_pcache && |
| (url_pattern.find('*') == StringPiece::npos)) { |
| // We could use Wildcard::IsSimple but let's define ? to mean in this |
| // context a literal '?' because query-params are way more common than |
| // single-char matching. |
| PurgeUrl(url_pattern, timestamp_ms); |
| } else { |
| if (!url_cache_invalidation_entries_.empty()) { |
| // Check that this Add preserves the invariant that |
| // url_cache_invalidation_entries_ is sorted on timestamp_ms. |
| if (url_cache_invalidation_entries_.back()->timestamp_ms > timestamp_ms) { |
| LOG(DFATAL) << "Timestamp " << timestamp_ms << " is less than the last " |
| << "timestamp already added: " |
| << url_cache_invalidation_entries_.back()->timestamp_ms; |
| return; |
| } |
| } |
| url_cache_invalidation_entries_.push_back( |
| new UrlCacheInvalidationEntry(url_pattern, timestamp_ms, |
| ignores_metadata_and_pcache)); |
| } |
| } |
| |
| bool RewriteOptions::UpdateCacheInvalidationTimestampMs(int64 timestamp_ms) { |
| ScopedMutex lock(cache_purge_mutex_.get()); |
| DCHECK_LT(0, timestamp_ms); |
| bool ret = false; |
| if (purge_set_->global_invalidation_timestamp_ms() < timestamp_ms) { |
| bool recompute_signature = ClearSignatureWithCaution(); |
| ret = purge_set_.MakeWriteable()->UpdateGlobalInvalidationTimestampMs( |
| timestamp_ms); |
| Modify(); |
| if (recompute_signature) { |
| signature_.clear(); |
| ComputeSignatureLockHeld(); |
| } |
| } |
| return ret; |
| } |
| |
| int64 RewriteOptions::cache_invalidation_timestamp() const { |
| ThreadSystem::ScopedReader lock(cache_purge_mutex_.get()); |
| DCHECK(purge_set_->has_global_invalidation_timestamp_ms()); |
| return purge_set_->global_invalidation_timestamp_ms(); |
| } |
| |
| bool RewriteOptions::has_cache_invalidation_timestamp_ms() const { |
| ThreadSystem::ScopedReader lock(cache_purge_mutex_.get()); |
| return purge_set_->has_global_invalidation_timestamp_ms(); |
| } |
| |
| bool RewriteOptions::UpdateCachePurgeSet( |
| const CopyOnWrite<PurgeSet>& purge_set) { |
| bool ret = false; |
| ScopedMutex lock(cache_purge_mutex_.get()); |
| if (purge_set_.get() != purge_set.get()) { |
| bool recompute_signature = ClearSignatureWithCaution(); |
| purge_set_ = purge_set; |
| Modify(); |
| if (recompute_signature) { |
| signature_.clear(); |
| ComputeSignatureLockHeld(); |
| } |
| ret = true; |
| } |
| return ret; |
| } |
| |
| GoogleString RewriteOptions::PurgeSetString() const { |
| ScopedMutex lock(cache_purge_mutex_.get()); |
| return purge_set_->ToString(); |
| } |
| |
| bool RewriteOptions::IsUrlCacheInvalidationEntriesSorted() const { |
| for (int i = 0, n = url_cache_invalidation_entries_.size(); i < n - 1; ++i) { |
| if (url_cache_invalidation_entries_[i]->timestamp_ms > |
| url_cache_invalidation_entries_[i + 1]->timestamp_ms) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| HttpOptions RewriteOptions::ComputeHttpOptions() const { |
| HttpOptions options; |
| options.respect_vary = respect_vary(); |
| options.implicit_cache_ttl_ms = implicit_cache_ttl_ms(); |
| options.min_cache_ttl_ms = min_cache_ttl_ms(); |
| return options; |
| } |
| |
| bool RewriteOptions::CacheFragmentOption::SetFromString( |
| StringPiece value, GoogleString* error_detail) { |
| // The main thing here is that the fragment not contain '/' (the separator |
| // used by HTTPCache) or '.' (so that a fragment can't be confused for a Host: |
| // header) but use a whitelist to be on the safe side. |
| for (int i = 0, n = value.length(); i < n; ++i) { |
| const char c = value.data()[i]; |
| if (!IsAsciiAlphaNumeric(c) && c != '-' && c != '_') { |
| *error_detail = ("A CacheFragment must be only letters, numbers, " |
| "underscores and hyphens. Found '"); |
| *error_detail += c; |
| *error_detail += "'."; |
| return false; |
| } |
| } |
| set(value.as_string()); |
| return true; |
| } |
| |
| GoogleString RewriteOptions::ScopeEnumToString(OptionScope scope) { |
| switch (scope) { |
| case kQueryScope: |
| return "Query"; |
| case kDirectoryScope: |
| return "Directory"; |
| case kServerScope: |
| return "Server"; |
| case kProcessScope: |
| return "Process"; |
| case kProcessScopeStrict: |
| return "Process Strict"; |
| default: |
| return "Unknown"; |
| } |
| } |
| |
| bool RewriteOptions::CheckLevelSpecificOption( |
| RewriteLevel rewrite_level, const Option<bool>& option) const { |
| if (option.was_set() || (level() != rewrite_level)) { |
| return option.value(); |
| } |
| return true; |
| } |
| |
| int64 RewriteOptions::ImageJpegQuality() const { |
| int64 quality = image_jpeg_recompress_quality_.value(); |
| if (quality < 0) { |
| quality = image_recompress_quality_.value(); |
| } |
| return quality; |
| } |
| |
| int64 RewriteOptions::ImageJpegQualityForSmallScreen() const { |
| int64 quality = image_jpeg_recompress_quality_for_small_screens_.value(); |
| if (quality < 0) { |
| quality = ImageJpegQuality(); |
| } |
| return quality; |
| } |
| |
| int64 RewriteOptions::ImageJpegQualityForSaveData() const { |
| int64 quality = image_jpeg_quality_for_save_data_.value(); |
| if (quality < 0) { |
| quality = ImageJpegQuality(); |
| } |
| return quality; |
| } |
| |
| int64 RewriteOptions::ImageWebpQuality() const { |
| int64 quality = image_webp_recompress_quality_.value(); |
| if (quality < 0) { |
| quality = image_recompress_quality_.value(); |
| } |
| return quality; |
| } |
| |
| int64 RewriteOptions::ImageWebpQualityForSmallScreen() const { |
| int64 quality = image_webp_recompress_quality_for_small_screens_.value(); |
| if (quality < 0) { |
| quality = ImageWebpQuality(); |
| } |
| return quality; |
| } |
| |
| int64 RewriteOptions::ImageWebpQualityForSaveData() const { |
| int64 quality = image_webp_quality_for_save_data_.value(); |
| if (quality < 0) { |
| quality = ImageWebpQuality(); |
| } |
| return quality; |
| } |
| |
| int64 RewriteOptions::ImageWebpAnimatedQuality() const { |
| int64 quality = image_webp_animated_recompress_quality_.value(); |
| if (quality < 0) { |
| quality = ImageWebpQuality(); |
| } |
| return quality; |
| } |
| |
| int64 RewriteOptions::ImageJpegNumProgressiveScansForSmallScreen() const { |
| int64 num = image_jpeg_num_progressive_scans_for_small_screens_.value(); |
| if (num < 0) { |
| num = image_jpeg_num_progressive_scans_.value(); |
| } |
| return num; |
| } |
| |
| bool RewriteOptions::HasValidSmallScreenQualities() const { |
| return (ImageWebpQualityForSmallScreen() != ImageWebpQuality() || |
| ImageJpegQualityForSmallScreen() != ImageJpegQuality()); |
| } |
| |
| bool RewriteOptions::HasValidSaveDataQualities() const { |
| return (ImageWebpQualityForSaveData() != ImageWebpQuality() || |
| ImageJpegQualityForSaveData() != ImageJpegQuality()); |
| } |
| |
| } // namespace net_instaweb |