Chromium Code Reviews| Index: webkit/media/buffered_resource_loader.cc |
| diff --git a/webkit/media/buffered_resource_loader.cc b/webkit/media/buffered_resource_loader.cc |
| index 14ca0a12625058e61d70919b3de945f23cb05cbc..2afc2ac350dc657da8893c5e7e1104dd367f6ed7 100644 |
| --- a/webkit/media/buffered_resource_loader.cc |
| +++ b/webkit/media/buffered_resource_loader.cc |
| @@ -4,13 +4,18 @@ |
| #include "webkit/media/buffered_resource_loader.h" |
| +#include <math.h> |
| + |
| #include "base/callback_helpers.h" |
| #include "base/format_macros.h" |
| +#include "base/metrics/histogram.h" |
| #include "base/string_number_conversions.h" |
| #include "base/string_util.h" |
| #include "base/stringprintf.h" |
| #include "media/base/media_log.h" |
| #include "net/http/http_request_headers.h" |
| +#include "net/http/http_util.h" |
| +#include "net/http/http_version.h" |
| #include "third_party/WebKit/Source/WebKit/chromium/public/WebKit.h" |
| #include "third_party/WebKit/Source/WebKit/chromium/public/WebURLLoaderOptions.h" |
| #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebKitPlatformSupport.h" |
| @@ -18,6 +23,8 @@ |
| #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebURLError.h" |
| #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebURLResponse.h" |
| +using base::Time; |
| +using base::TimeDelta; |
| using WebKit::WebFrame; |
| using WebKit::WebString; |
| using WebKit::WebURLError; |
| @@ -111,6 +118,7 @@ BufferedResourceLoader::BufferedResourceLoader( |
| : buffer_(kMinBufferCapacity, kMinBufferCapacity), |
| loader_failed_(false), |
| defer_strategy_(strategy), |
| + might_be_reused_from_cache_in_future_(true), |
| range_supported_(false), |
| saved_forward_capacity_(0), |
| url_(url), |
| @@ -352,6 +360,96 @@ void BufferedResourceLoader::didSendData( |
| NOTIMPLEMENTED(); |
| } |
| +// Reasons that a cached WebURLResponse will *not* prevent a future request to |
| +// the server. |
| +enum UncacheableReason { |
| + kNoData = 1, // Not 200 or 206. |
| + kPre11PartialResponse = 2, // 206 but HTTP version < 1.1. |
| + kNoStrongValidatorOnPartialResponse = 3, // 206, no strong validator. |
| + kNoValidatorOnFullResponse = 4, // 200, no strong or weak validator. |
| + kShortMaxAge = 5, // Max age less than 1h (arbitrary value). |
| + kExpiresTooSoon = 6, // Expires in less than 1h (arbitrary value). |
| + kHasMustRevalidate = 7, // Response asks for revalidation. |
| + kNoCache = 8, // Response included a no-cache header. |
| + kNoStore = 9, // Response included a no-store header. |
| + kMaxReason // Needs to be one more than max legitimate reason. |
| +}; |
| + |
| +// Return true if "response" might be used for a future request (using the disk |
| +// cache). This is basically a laundry list of reasons we *know* the response |
| +// won't be useful in the future. |
| +static bool MightBeReusedFromCacheInFuture(const WebURLResponse& response) { |
|
darin (slow to review)
2012/05/22 05:30:59
style nit: usually best not to interrupt the defin
|
| + std::vector<UncacheableReason> reasons; |
|
darin (slow to review)
2012/05/22 05:30:59
what's the point of building up this vector? why
|
| + const int code = response.httpStatusCode(); |
| + const int version = response.httpVersion(); |
| + const net::HttpVersion http_version = |
| + version == WebURLResponse::HTTP_1_1 ? net::HttpVersion(1, 1) : |
| + version == WebURLResponse::HTTP_1_0 ? net::HttpVersion(1, 0) : |
| + version == WebURLResponse::HTTP_0_9 ? net::HttpVersion(0, 9) : |
| + net::HttpVersion(); |
| + if (code != kHttpOK && code != kHttpPartialContent) |
| + reasons.push_back(kNoData); |
| + if (http_version < net::HttpVersion(1, 1) && code == kHttpPartialContent) |
| + reasons.push_back(kPre11PartialResponse); |
| + if (!net::HttpUtil::HasStrongValidators( |
| + http_version, |
| + response.httpHeaderField("etag").utf8(), |
| + response.httpHeaderField("Last-Modified").utf8(), |
| + response.httpHeaderField("Date").utf8())) { |
| + if (code == kHttpPartialContent) |
| + reasons.push_back(kNoStrongValidatorOnPartialResponse); |
| + else if (code == kHttpOK) |
| + reasons.push_back(kNoValidatorOnFullResponse); |
| + } |
| + |
| + std::string cache_control_header = |
| + response.httpHeaderField("cache-control").utf8(); |
| + StringToLowerASCII(&cache_control_header); |
| + if (cache_control_header.find("no-cache") != std::string::npos) |
| + reasons.push_back(kNoCache); |
| + if (cache_control_header.find("no-store") != std::string::npos) |
| + reasons.push_back(kNoStore); |
| + if (cache_control_header.find("must-revalidate") != std::string::npos) |
| + reasons.push_back(kHasMustRevalidate); |
| + |
| + const TimeDelta kMinimumAgeForUsefulness = |
| + TimeDelta::FromSeconds(3600); // Arbitrary value. |
| + |
| + const char kMaxAgePrefix[] = "max-age="; |
| + const size_t kMaxAgePrefixLen = arraysize(kMaxAgePrefix) - 1; |
| + if (LowerCaseEqualsASCII( |
| + cache_control_header.begin(), |
| + cache_control_header.begin() + kMaxAgePrefixLen, |
| + kMaxAgePrefix)) { |
| + int64 max_age_seconds; |
| + base::StringToInt64( |
| + base::StringPiece( |
| + cache_control_header.begin() + kMaxAgePrefixLen, |
| + cache_control_header.end()), |
| + &max_age_seconds); |
| + if (TimeDelta::FromSeconds(max_age_seconds) < kMinimumAgeForUsefulness) |
| + reasons.push_back(kShortMaxAge); |
| + } |
| + |
| + Time date; |
| + Time expires; |
| + if (Time::FromString(response.httpHeaderField("Date").utf8().data(), &date) && |
| + Time::FromString( |
| + response.httpHeaderField("Expires").utf8().data(), &expires) && |
| + date > Time() && expires > Time() && |
| + (expires - date) < kMinimumAgeForUsefulness) { |
| + reasons.push_back(kExpiresTooSoon); |
| + } |
| + |
| + UMA_HISTOGRAM_BOOLEAN("Media.CacheUseful", reasons.empty()); |
| + for (size_t i = 0; i < reasons.size(); ++i) { |
| + UMA_HISTOGRAM_ENUMERATION( |
| + "Media.UncacheableReason", reasons[i], kMaxReason); |
| + } |
| + |
| + return reasons.empty(); |
| +} |
| + |
| void BufferedResourceLoader::didReceiveResponse( |
| WebURLLoader* loader, |
| const WebURLResponse& response) { |
| @@ -368,6 +466,9 @@ void BufferedResourceLoader::didReceiveResponse( |
| if (start_cb_.is_null()) |
| return; |
| + might_be_reused_from_cache_in_future_ = |
| + MightBeReusedFromCacheInFuture(response); |
| + |
| // Expected content length can be |kPositionNotSpecified|, in that case |
| // |content_length_| is not specified and this is a streaming response. |
| content_length_ = response.expectedContentLength(); |
| @@ -540,6 +641,8 @@ bool BufferedResourceLoader::HasSingleOrigin() const { |
| } |
| void BufferedResourceLoader::UpdateDeferStrategy(DeferStrategy strategy) { |
| + if (!might_be_reused_from_cache_in_future_ && strategy == kNeverDefer) |
| + strategy = kThresholdDefer; |
| defer_strategy_ = strategy; |
| UpdateDeferBehavior(); |
| } |