blob: cbc95adafdc59e8620ce061fdcfca322d71415f0 [file] [log] [blame]
/*
* Copyright 2011 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: nforman@google.com (Naomi Forman)
//
// Implements the convert_meta_tags filter, which creates a
// response header for http-equiv meta tags.
#include "net/instaweb/rewriter/public/meta_tag_filter.h"
#include "net/instaweb/rewriter/public/rewrite_driver.h"
#include "pagespeed/kernel/base/statistics.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/kernel/http/content_type.h"
#include "pagespeed/kernel/http/response_headers.h"
namespace {
// Statistics variable for number of tags converted to headers.
const char kConvertedMetaTags[] = "converted_meta_tags";
} // namespace
namespace net_instaweb {
MetaTagFilter::MetaTagFilter(RewriteDriver* rewrite_driver)
: CommonFilter(rewrite_driver),
response_headers_(NULL) {
Statistics* stats = driver()->statistics();
converted_meta_tag_count_ = stats->GetVariable(kConvertedMetaTags);
}
void MetaTagFilter::InitStats(Statistics* stats) {
stats->AddVariable(kConvertedMetaTags);
}
MetaTagFilter::~MetaTagFilter() {}
void MetaTagFilter::StartDocumentImpl() {
// This pointer will be nulled at first Flush to guarantee that we don't
// write it after that (won't work).
response_headers_ = driver()->mutable_response_headers();
}
void MetaTagFilter::EndElementImpl(HtmlElement* element) {
// If response_headers_ are null, they got reset due to a flush, so don't
// try to convert any tags into response_headers_ (which were already
// finalized). Also don't add meta tags to response_headers_ if they're
// inside a noscript tag.
if (response_headers_ == NULL || noscript_element() != NULL ||
element->keyword() != HtmlName::kMeta) {
return;
}
if (ExtractAndUpdateMetaTagDetails(element, response_headers_)) {
converted_meta_tag_count_->Add(1);
}
}
bool MetaTagFilter::ExtractAndUpdateMetaTagDetails(
HtmlElement* element,
ResponseHeaders* response_headers) {
if (response_headers == NULL) {
return false;
}
GoogleString content, mime_type, charset;
if (ExtractMetaTagDetails(*element, response_headers,
&content, &mime_type, &charset)) {
if (!content.empty()) {
// Yes content => it has http-equiv and content attributes,
// and a mime_type and/or a charset, but we need a mime_type.
if (!mime_type.empty()) {
const ContentType* type = MimeTypeToContentType(mime_type);
// We only want to propagate the charset for HTML;
// XHTML is forced to UTF-8 anyway and we really don't want to propagate
// an XHTML type in cases where Apache is unsure just to propagate
// a charset that's not supposed to take any effect.
if (type != NULL && type->type() == ContentType::kHtml) {
if (response_headers->MergeContentType(content)) {
return true;
}
}
}
} else {
// No content => it has a charset attribute (and no mime_type).
GoogleString type = StrCat("; charset=", charset);
if (response_headers->MergeContentType(type)) {
return true;
}
}
}
return false;
}
void MetaTagFilter::Flush() {
response_headers_ = NULL;
}
} // namespace net_instaweb