| /* |
| * 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 |