blob: 8f7f25755487dc65933e7eaa2c4811ea47f9d3a7 [file] [log] [blame]
/**
* Copyright 2010 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: jmarantz@google.com (Joshua Marantz)
// and sligocki@google.com (Shawn Ligocki)
// Unit-test the html rewriter
#include "base/scoped_ptr.h"
#include "net/instaweb/rewriter/public/css_filter.h"
#include "net/instaweb/rewriter/public/css_move_to_head_filter.h"
#include "net/instaweb/rewriter/public/css_tag_scanner.h"
#include "net/instaweb/rewriter/public/img_tag_scanner.h"
#include "net/instaweb/rewriter/public/resource_manager.h"
#include "net/instaweb/rewriter/public/resource_manager_test_base.h"
#include <string>
namespace net_instaweb {
class CssFilterTest : public ResourceManagerTestBase {
protected:
// Check that inline CSS get's rewritten correctly.
void ValidateRewriteInlineCss(const char* id,
const StringPiece& css_input,
const StringPiece& css_expected_output) {
rewrite_driver_.AddFilter(RewriteOptions::kRewriteCss);
static const char prefix[] =
"<head>\n"
" <title>Example style outline</title>\n"
" <!-- Style starts here -->\n"
" <style type='text/css'>";
static const char suffix[] = "</style>\n"
" <!-- Style ends here -->\n"
"</head>";
std::string html_input = StrCat(prefix, css_input, suffix);
std::string html_output = StrCat(prefix, css_expected_output, suffix);
ValidateExpected(id, html_input, html_output);
}
void ValidateNoChangeRewriteInlineCss(const char* id,
const StringPiece& css_input) {
ValidateRewriteInlineCss(id, css_input, css_input);
}
};
TEST_F(CssFilterTest, SimpleRewriteCssTest) {
std::string input_style =
"background_blue { background-color: #f00; }\n"
"foreground_yellow { color: yellow; }\n";
std::string output_style =
"background_blue{background-color:red}"
"foreground_yellow{color:#ff0}";
ValidateRewriteInlineCss("rewrite_css", input_style, output_style);
}
// Make sure we can deal with 0 character nodes between open and close of style.
TEST_F(CssFilterTest, RewriteEmptyCssTest) {
ValidateRewriteInlineCss("rewrite_empty_css", "", "");
}
// Make sure we don't change CSS with errors. Note: We can move these tests
// to expected rewrites if we find safe ways to edit them.
TEST_F(CssFilterTest, NoRewriteError) {
ValidateNoChangeRewriteInlineCss("non_unicode_charset",
"a { font-family: \"\xCB\xCE\xCC\xE5\"; }");
// From http://www.baidu.com/
ValidateNoChangeRewriteInlineCss("non_unicode_baidu",
"#lk span {font:14px \"\xCB\xCE\xCC\xE5\"}");
// From http://www.yahoo.com/
const char confusing_value[] =
"a { background-image:-webkit-gradient(linear, 50% 0%, 50% 100%,"
" from(rgb(232, 237, 240)), to(rgb(252, 252, 253)));}";
ValidateNoChangeRewriteInlineCss("non_standard_value", confusing_value);
ValidateNoChangeRewriteInlineCss("bad_char_in_selector",
".bold: { font-weight: bold }");
}
TEST_F(CssFilterTest, RewriteVariousCss) {
// Distilled examples.
const char* examples[] = {
"a.b #c.d e#d,f:g>h+i>j{color:red}", // .#,>+: in selectors
"a{border:solid 1px #ccc}", // Multiple values declaration
"a{border:none!important}", // !important
"a{background-image:url(foo.png)}", // url
"a{background-position:-19px 60%}", // negative position
"a{margin:0}", // 0 w/ no units
"a{padding:0.01em 0.25em}", // fractions and em
"a{-moz-border-radius-topleft:0}", // Browser-specific (-moz)
"a{background:none}", // CSS Parser used to expand this.
};
for (int i = 0; i < arraysize(examples); ++i) {
ValidateNoChangeRewriteInlineCss("distilled_css", examples[i]);
}
}
// Test more complicated CSS.
TEST_F(CssFilterTest, ComplexCssTest) {
// Real-world examples. Picked out of Wikipedia's CSS.
const char* examples[][2] = {
{ "#userlogin, #userloginForm {\n"
" border: solid 1px #cccccc;\n"
" padding: 1.2em;\n"
" float: left;\n"
"}\n",
"#userlogin,#userloginForm{border:solid 1px #ccc;padding:1.2em;"
"float:left}"},
{ "h3 .editsection { font-size: 76%; font-weight: normal; }\n",
"h3 .editsection{font-size:76%;font-weight:normal}"},
{ "div.magnify a, div.magnify img {\n"
" display: block;\n"
" border: none !important;\n"
" background: none !important;\n"
"}\n",
"div.magnify a,div.magnify img{display:block;border:none!important;"
"background:none!important}"},
{ "#ca-watch.icon a:hover {\n"
" background-image: url('images/watch-icons.png?1');\n"
" background-position: -19px 60%;\n"
"}\n",
"#ca-watch.icon a:hover{background-image:url(images/watch-icons.png?1);"
"background-position:-19px 60%}"},
{ "body {\n"
" background: White;\n"
" /*font-size: 11pt !important;*/\n"
" color: Black;\n"
" margin: 0;\n"
" padding: 0;\n"
"}\n",
"body{background:#fff;color:#000;margin:0;padding:0}"},
{ ".suggestions-result{\n"
" color:black;\n"
" color:WindowText;\n"
" padding:0.01em 0.25em;\n"
"}\n",
// TODO(sligocki): Do we care about color:WindowText?
//".suggestions-result{color:#000;color:WindowText;padding:0.01em 0.25em}"
".suggestions-result{color:#000;color:#000;padding:0.01em 0.25em}"},
{ ".ui-corner-tl { -moz-border-radius-topleft: 0; -webkit-border-top-left"
"-radius: 0; }\n",
".ui-corner-tl{-moz-border-radius-topleft:0;-webkit-border-top-left"
"-radius:0}"},
{ ".ui-tabs .ui-tabs-nav li.ui-tabs-selected a, .ui-tabs .ui-tabs-nav li."
"ui-state-disabled a, .ui-tabs .ui-tabs-nav li.ui-state-processing a { "
"cursor: pointer; }\n",
".ui-tabs .ui-tabs-nav li.ui-tabs-selected a,.ui-tabs .ui-tabs-nav "
"li.ui-state-disabled a,.ui-tabs .ui-tabs-nav li.ui-state-processing a{"
"cursor:pointer}"},
{ ".ui-datepicker-cover {\n"
" display: none; /*sorry for IE5*/\n"
" display/**/: block; /*sorry for IE5*/\n"
" position: absolute; /*must have*/\n"
" z-index: -1; /*must have*/\n"
" filter: mask(); /*must have*/\n"
" top: -4px; /*must have*/\n"
" left: -4px; /*must have*/\n"
" width: 200px; /*must have*/\n"
" height: 200px; /*must have*/\n"
"}\n",
// TODO(sligocki): Do we care about stripping /**/?
// Comment says it's for IE5.
//".ui-datepicker-cover{display:none;display/**/:block;position:absolute;"
//"z-index:-1;filter:mask();top:-4px;left:-4px;width:200px;height:200px}"
".ui-datepicker-cover{display:none;display:block;position:absolute;"
"z-index:-1;filter:mask();top:-4px;left:-4px;width:200px;height:200px}"}
};
for (int i = 0; i < arraysize(examples); ++i) {
ValidateRewriteInlineCss("complex_css", examples[i][0], examples[i][1]);
}
}
} // namespace net_instaweb