blob: 5201443274eb8ec945f6e102760f111b62e30ff4 [file] [log] [blame]
/*
* Copyright 2013 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.
*/
// Author: morlovich@google.com (Maksim Orlovich)
#include "net/instaweb/rewriter/public/critical_selector_finder.h"
#include <map>
#include "base/logging.h"
#include "net/instaweb/rewriter/critical_keys.pb.h"
#include "net/instaweb/rewriter/public/critical_finder_support_util.h"
#include "net/instaweb/rewriter/public/property_cache_util.h"
#include "net/instaweb/rewriter/public/rewrite_driver.h"
#include "net/instaweb/rewriter/public/rewrite_options.h"
#include "net/instaweb/rewriter/public/server_context.h"
#include "net/instaweb/util/public/property_cache.h"
#include "pagespeed/kernel/base/message_handler.h"
#include "pagespeed/kernel/base/scoped_ptr.h"
#include "pagespeed/kernel/base/statistics.h"
#include "pagespeed/kernel/base/string.h"
#include "pagespeed/kernel/base/string_util.h"
namespace net_instaweb {
const char CriticalSelectorFinder::kCriticalSelectorsPropertyName[] =
"critical_selectors";
const char CriticalSelectorFinder::kCriticalSelectorsValidCount[] =
"critical_selectors_valid_count";
const char CriticalSelectorFinder::kCriticalSelectorsExpiredCount[] =
"critical_selectors_expired_count";
const char CriticalSelectorFinder::kCriticalSelectorsNotFoundCount[] =
"critical_selectors_not_found_count";
CriticalSelectorFinder::CriticalSelectorFinder(
const PropertyCache::Cohort* cohort, NonceGenerator* nonce_generator,
Statistics* statistics)
: cohort_(cohort), nonce_generator_(nonce_generator) {
critical_selectors_valid_count_ = statistics->GetTimedVariable(
kCriticalSelectorsValidCount);
critical_selectors_expired_count_ = statistics->GetTimedVariable(
kCriticalSelectorsExpiredCount);
critical_selectors_not_found_count_ = statistics->GetTimedVariable(
kCriticalSelectorsNotFoundCount);
}
CriticalSelectorFinder::~CriticalSelectorFinder() {
}
void CriticalSelectorFinder::InitStats(Statistics* statistics) {
statistics->AddTimedVariable(kCriticalSelectorsValidCount,
ServerContext::kStatisticsGroup);
statistics->AddTimedVariable(kCriticalSelectorsExpiredCount,
ServerContext::kStatisticsGroup);
statistics->AddTimedVariable(kCriticalSelectorsNotFoundCount,
ServerContext::kStatisticsGroup);
}
bool CriticalSelectorFinder::IsCriticalSelector(RewriteDriver* driver,
const GoogleString& selector) {
const StringSet& critical_selectors = GetCriticalSelectors(driver);
return (critical_selectors.find(selector) != critical_selectors.end());
}
const StringSet& CriticalSelectorFinder::GetCriticalSelectors(
RewriteDriver* driver) {
UpdateCriticalSelectorInfoInDriver(driver);
return driver->critical_selector_info()->critical_selectors;
}
void CriticalSelectorFinder::WriteCriticalSelectorsToPropertyCache(
const StringSet& selector_set, StringPiece nonce, RewriteDriver* driver) {
DCHECK(cohort_ != NULL);
WriteCriticalSelectorsToPropertyCacheStatic(
selector_set, nonce, SupportInterval(), ShouldReplacePriorResult(),
driver->server_context()->page_property_cache(), cohort_,
driver->property_page(), driver->message_handler(), driver->timer());
}
void CriticalSelectorFinder::WriteCriticalSelectorsToPropertyCacheStatic(
const StringSet& selector_set, StringPiece nonce, int support_interval,
bool should_replace_prior_result, const PropertyCache* cache,
const PropertyCache::Cohort* cohort, AbstractPropertyPage* page,
MessageHandler* message_handler, Timer* timer) {
CriticalKeysWriteFlags flags;
if (should_replace_prior_result) {
flags = kReplacePriorResult;
} else {
flags = kRequirePriorSupport;
}
WriteCriticalKeysToPropertyCache(
selector_set, nonce, support_interval, flags,
kCriticalSelectorsPropertyName, cache, cohort, page, message_handler,
timer);
}
void CriticalSelectorFinder::UpdateCriticalSelectorInfoInDriver(
RewriteDriver* driver) {
if (driver->critical_selector_info() != NULL) {
return;
}
PropertyCacheDecodeResult result;
// NOTE: if any of these checks fail you probably didn't set up your test
// environment carefully enough. Figuring that out based on test failures
// alone will drive you nuts and take hours out of your life, thus DCHECKs.
DCHECK(driver != NULL);
DCHECK(cohort_ != NULL);
scoped_ptr<CriticalKeys> critical_keys(DecodeFromPropertyCache<CriticalKeys>(
driver, cohort_, kCriticalSelectorsPropertyName,
driver->options()->finder_properties_cache_expiration_time_ms(),
&result));
switch (result) {
case kPropertyCacheDecodeNotFound:
critical_selectors_not_found_count_->IncBy(1);
break;
case kPropertyCacheDecodeExpired:
critical_selectors_expired_count_->IncBy(1);
break;
case kPropertyCacheDecodeParseError:
driver->message_handler()->Message(
kWarning, "Unable to parse Critical Selectors PropertyValue; "
"url: %s", driver->url());
break;
case kPropertyCacheDecodeOk:
critical_selectors_valid_count_->IncBy(1);
}
// Create a placeholder CriticalKeys to use in case the call to
// DecodeFromPropertyCache above returned NULL.
CriticalKeys static_keys;
CriticalKeys* keys_to_use =
(critical_keys == NULL) ? &static_keys : critical_keys.get();
CriticalSelectorInfo* critical_selector_info = new CriticalSelectorInfo;
critical_selector_info->proto = *keys_to_use;
GetCriticalKeysFromProto(0 /* support_percentage */, *keys_to_use,
&critical_selector_info->critical_selectors);
driver->set_critical_selector_info(critical_selector_info);
}
BeaconMetadata CriticalSelectorFinder::PrepareForBeaconInsertion(
const StringSet& selectors, RewriteDriver* driver) {
UpdateCriticalSelectorInfoInDriver(driver);
BeaconMetadata result;
result.status = kDoNotBeacon;
if (selectors.empty()) {
return result;
}
if (ShouldReplacePriorResult()) {
// The computed critical keys will not require a nonce as we trust all
// beacon results.
result.status = kBeaconNoNonce;
return result;
}
// Avoid memory copy by capturing computed_nonce using RVA and swapping the
// two strings.
CriticalKeys& proto = driver->critical_selector_info()->proto;
// If the candidate keys changed, force a rebeacon by clearing the next beacon
// timestamp.
::net_instaweb::UpdateCandidateKeys(selectors, &proto, true);
net_instaweb::PrepareForBeaconInsertionHelper(
&proto, nonce_generator_, driver,
true /* using_candidate_key_detection */, &result);
if (result.status != kDoNotBeacon) {
DCHECK(cohort_ != NULL);
UpdateInPropertyCache(proto, cohort_, kCriticalSelectorsPropertyName,
true /* write_cohort */, driver->property_page());
}
return result;
}
void
BeaconCriticalSelectorFinder::WriteCriticalSelectorsToPropertyCacheFromBeacon(
const StringSet& selector_set, StringPiece nonce,
const PropertyCache* cache, const PropertyCache::Cohort* cohort,
AbstractPropertyPage* page, MessageHandler* message_handler, Timer* timer) {
return CriticalSelectorFinder::WriteCriticalSelectorsToPropertyCacheStatic(
selector_set, nonce, kDefaultSupportInterval, false, cache, cohort, page,
message_handler, timer);
}
} // namespace net_instaweb