blob: 76ed4841e6e8c7c564064015eb0f41214a85ad5b [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: sriharis@google.com (Srihari Sukumaran)
#include "net/instaweb/rewriter/public/fix_reflow_filter.h"
#include <map>
#include <memory>
#include <utility>
#include "base/logging.h"
#include "net/instaweb/http/public/log_record.h"
#include "net/instaweb/rewriter/public/js_defer_disabled_filter.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/basictypes.h"
#include "pagespeed/kernel/base/string.h"
#include "pagespeed/kernel/base/string_util.h"
#include "pagespeed/kernel/html/html_element.h"
#include "pagespeed/kernel/html/html_name.h"
#include "pagespeed/opt/logging/enums.pb.h"
namespace {
const char kReflowValueSeparators[] = ",:";
const char kReflowClassAttribute[] = "data-pagespeed-fix-reflow";
} // namespace
namespace net_instaweb {
const char FixReflowFilter::kElementRenderedHeightPropertyName[] =
"element_rendered_height";
FixReflowFilter::FixReflowFilter(RewriteDriver* driver)
: rewrite_driver_(driver) {
}
FixReflowFilter::~FixReflowFilter() {
}
void FixReflowFilter::DetermineEnabled(GoogleString* disabled_reason) {
set_is_enabled(JsDeferDisabledFilter::ShouldApply(rewrite_driver_));
if (!is_enabled()) {
rewrite_driver_->log_record()->LogRewriterHtmlStatus(
RewriteOptions::FilterId(RewriteOptions::kFixReflows),
RewriterHtmlApplication::DISABLED);
}
}
void FixReflowFilter::StartDocument() {
bool pcache_miss = true;
PropertyPage* page = rewrite_driver_->property_page();
const PropertyCache::Cohort* cohort =
rewrite_driver_->server_context()->fix_reflow_cohort();
if (page != NULL && cohort != NULL) {
PropertyValue* property_value = page->GetProperty(
cohort, kElementRenderedHeightPropertyName);
VLOG(1) << "Property value: " << property_value << " has value? "
<< property_value->has_value();
const int64 cache_ttl_ms = rewrite_driver_->options()->
finder_properties_cache_expiration_time_ms();
PropertyCache* property_cache =
rewrite_driver_->server_context()->page_property_cache();
if (property_value != NULL &&
property_value->has_value() &&
!property_cache->IsExpired(property_value, cache_ttl_ms)) {
pcache_miss = false;
VLOG(1) << "FixReflowFilter. Valid value in pcache.";
// Parse property_value->value() into "id:height" and keep these locally.
StringPieceVector element_height_vector;
SplitStringPieceToVector(
property_value->value(), kReflowValueSeparators,
&element_height_vector, true);
for (int i = 0, n = element_height_vector.size(); i < n - 1; i += 2) {
element_height_map_.insert(make_pair(
element_height_vector[i].as_string(),
element_height_vector[i+1].as_string()));
}
}
}
if (pcache_miss) {
rewrite_driver_->log_record()->LogRewriterHtmlStatus(
RewriteOptions::FilterId(RewriteOptions::kFixReflows),
RewriterHtmlApplication::PROPERTY_CACHE_MISS);
} else {
rewrite_driver_->log_record()->LogRewriterHtmlStatus(
RewriteOptions::FilterId(RewriteOptions::kFixReflows),
RewriterHtmlApplication::ACTIVE);
}
}
void FixReflowFilter::StartElement(HtmlElement* element) {
// See if element has attribute id matching any one from "id:height" pairs.
// If yes insert a style attribute with height.
if (element->keyword() == HtmlName::kDiv) {
const char* id = element->AttributeValue(HtmlName::kId);
if (id != NULL) {
ElementHeightMap::const_iterator i = element_height_map_.find(id);
if (i != element_height_map_.end()) {
rewrite_driver_->log_record()->SetRewriterLoggingStatus(
RewriteOptions::FilterId(RewriteOptions::kFixReflows),
RewriterApplication::APPLIED_OK);
VLOG(1) << "div " << id << " has height " << i->second;
element->AddAttribute(rewrite_driver_->MakeName(HtmlName::kStyle),
StrCat("min-height:", i->second),
HtmlElement::DOUBLE_QUOTE);
element->AddAttribute(rewrite_driver_->MakeName(kReflowClassAttribute),
"", HtmlElement::DOUBLE_QUOTE);
// TODO(sriharis): Should we add js to delete the added style
// attributes? Maybe a function that is called from js_defer.js's
// AfterDefer hook.
}
}
}
}
} // namespace net_instaweb