| /* |
| * 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: gagansingh@google.com (Gagan Singh) |
| |
| #include "net/instaweb/rewriter/public/js_disable_filter.h" |
| |
| #include "net/instaweb/http/public/log_record.h" |
| #include "net/instaweb/http/public/logging_proto_impl.h" |
| #include "net/instaweb/rewriter/public/rewrite_driver.h" |
| #include "net/instaweb/rewriter/public/rewrite_options.h" |
| #include "net/instaweb/rewriter/public/rewrite_test_base.h" |
| #include "testing/base/public/gunit.h" |
| #include "pagespeed/kernel/base/scoped_ptr.h" |
| #include "pagespeed/kernel/base/string.h" |
| #include "pagespeed/kernel/base/string_util.h" |
| #include "pagespeed/kernel/base/string_writer.h" |
| #include "pagespeed/kernel/http/user_agent_matcher_test_base.h" |
| #include "pagespeed/opt/logging/enums.pb.h" |
| |
| namespace net_instaweb { |
| |
| namespace { |
| |
| const char kUnrelatedNoscriptTags[] = |
| "<noscript>This is original noscript tag</noscript>"; |
| const char kUnrelatedTags[] = |
| "<div id=\"contentContainer\">" |
| "<h1>Hello 1</h1>" |
| "<div id=\"middleFooter\"><h3>Hello 3</h3></div>" |
| "</div>"; |
| const char kXUACompatibleMetaTag[] = |
| "<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">"; |
| |
| } // namespace |
| |
| |
| class JsDisableFilterTest : public RewriteTestBase { |
| protected: |
| virtual void SetUp() { |
| options_->EnableFilter(RewriteOptions::kDisableJavascript); |
| options_->EnableFilter(RewriteOptions::kDeferJavascript); |
| options_->Disallow("*donotmove*"); |
| RewriteTestBase::SetUp(); |
| filter_.reset(new JsDisableFilter(rewrite_driver())); |
| rewrite_driver()->AddFilter(filter_.get()); |
| } |
| |
| virtual bool AddBody() const { |
| return false; |
| } |
| |
| void ExpectLogRecord(int index, int status, bool has_pagespeed_no_defer) { |
| AbstractLogRecord* log_record = rewrite_driver_->log_record(); |
| const RewriterInfo& rewriter_info = |
| log_record->logging_info()->rewriter_info(index); |
| EXPECT_EQ("jd", rewriter_info.id()); |
| EXPECT_EQ(status, rewriter_info.status()); |
| EXPECT_EQ(has_pagespeed_no_defer, |
| rewriter_info.rewrite_resource_info().has_pagespeed_no_defer()); |
| } |
| |
| scoped_ptr<JsDisableFilter> filter_; |
| }; |
| |
| TEST_F(JsDisableFilterTest, DisablesScript) { |
| const GoogleString input_html = StrCat( |
| "<body>", |
| kUnrelatedNoscriptTags, |
| "<script></script>" |
| "<script src=\"blah1\" random=\"true\">hi1</script>", |
| kUnrelatedTags, |
| "<img src=\"abc.jpg\" onload=\"foo1('abc');foo2();\">" |
| "<script src=\"blah2\" random=\"false\">hi2</script>" |
| "<script src=\"blah3\" data-pagespeed-no-defer=\"\"></script>" |
| "<script src=\"blah4\" pagespeed_no_defer=\"\"></script>" |
| "<script src=\"something-donotmove\"></script>" |
| "</body>"); |
| const GoogleString expected = StrCat( |
| "<body>", |
| kUnrelatedNoscriptTags, |
| "<script type=\"text/psajs\" orig_index=\"0\"></script>" |
| "<script src=\"blah1\" random=\"true\" type=\"text/psajs\"" |
| " orig_index=\"1\">hi1</script>", |
| kUnrelatedTags, StrCat( |
| "<img src=\"abc.jpg\" data-pagespeed-onload=\"foo1('abc');foo2();\" " |
| "onload=\"", JsDisableFilter::kElementOnloadCode, "\">" |
| "<script src=\"blah2\" random=\"false\"" |
| " type=\"text/psajs\" orig_index=\"2\">hi2</script>" |
| "<script src=\"blah3\" data-pagespeed-no-defer=\"\"></script>" |
| "<script src=\"blah4\" pagespeed_no_defer=\"\"></script>" |
| "<script src=\"something-donotmove\"></script>" |
| "</body>")); |
| |
| ValidateExpectedUrl("http://example.com/", input_html, expected); |
| ExpectLogRecord(0, RewriterApplication::APPLIED_OK, false); |
| ExpectLogRecord(1, RewriterApplication::APPLIED_OK, false); |
| ExpectLogRecord(2, RewriterApplication::APPLIED_OK, false); |
| ExpectLogRecord(3, RewriterApplication::APPLIED_OK, true); |
| ExpectLogRecord(4, RewriterApplication::APPLIED_OK, true); |
| ExpectLogRecord(5, RewriterApplication::APPLIED_OK, true); |
| rewrite_driver_->log_record()->WriteLog(); |
| for (int i = 0; i < logging_info()->rewriter_stats_size(); i++) { |
| if (logging_info()->rewriter_stats(i).id() == "jd" && |
| logging_info()->rewriter_stats(i).has_html_status()) { |
| EXPECT_EQ(RewriterHtmlApplication::ACTIVE, |
| logging_info()->rewriter_stats(i).html_status()); |
| const RewriteStatusCount& count_applied = |
| logging_info()->rewriter_stats(i).status_counts(0); |
| EXPECT_EQ(RewriterApplication::APPLIED_OK, |
| count_applied.application_status()); |
| EXPECT_EQ(6, count_applied.count()); |
| return; |
| } |
| } |
| FAIL(); |
| } |
| |
| TEST_F(JsDisableFilterTest, InvalidUserAgent) { |
| SetCurrentUserAgent("BlackListUserAgent"); |
| const char script[] = "<head>" |
| "<script " |
| "src='http://www.google.com/javascript/ajax_apis.js'></script>" |
| "<script" |
| "> func();</script>" |
| "</head><body>Hello, world!</body>"; |
| |
| ValidateNoChanges("defer_script", script); |
| rewrite_driver_->log_record()->WriteLog(); |
| for (int i = 0; i < logging_info()->rewriter_stats_size(); i++) { |
| if (logging_info()->rewriter_stats(i).id() == "jd" && |
| logging_info()->rewriter_stats(i).has_html_status()) { |
| EXPECT_EQ(RewriterHtmlApplication::USER_AGENT_NOT_SUPPORTED, |
| logging_info()->rewriter_stats(i).html_status()); |
| return; |
| } |
| } |
| FAIL(); |
| } |
| |
| TEST_F(JsDisableFilterTest, DisablesScriptWithExperimental) { |
| options()->set_enable_defer_js_experimental(true); |
| |
| const char kUnrelatedNoscriptTags[] = |
| "<noscript>This is original noscript tag</noscript>"; |
| const char kUnrelatedTags[] = |
| "<div id=\"contentContainer\">" |
| "<h1>Hello 1</h1>" |
| "<div id=\"middleFooter\"><h3>Hello 3</h3></div>" |
| "</div>"; |
| |
| const GoogleString input_html = StrCat( |
| "<body>", |
| kUnrelatedNoscriptTags, |
| "<script src=\"blah1\" random=\"true\">hi1</script>", |
| kUnrelatedTags, |
| "<script src=\"blah2\" random=\"false\">hi2</script>", |
| "</body>"); |
| const GoogleString expected = StrCat( |
| "<body>", |
| kUnrelatedNoscriptTags, |
| "<script src=\"blah1\" random=\"true\" type=\"text/psajs\"" |
| " orig_index=\"0\">hi1</script>", |
| kUnrelatedTags, |
| "<script src=\"blah2\" random=\"false\"" |
| " type=\"text/psajs\" orig_index=\"1\">hi2</script>" |
| "<script type=\"text/javascript\" data-pagespeed-no-defer>", |
| JsDisableFilter::kEnableJsExperimental, |
| "</script></body>"); |
| |
| ValidateExpectedUrl("http://example.com/", input_html, expected); |
| } |
| |
| TEST_F(JsDisableFilterTest, DisablesScriptWithQueryParam) { |
| const GoogleString input_html = StrCat( |
| kUnrelatedNoscriptTags, |
| "<script src=\"x?a=b&c=d\" random=\"true\">hi1</script>", |
| kUnrelatedTags, |
| "<script src=\"y?a=b&c=d\" random=\"false\">hi2</script>"); |
| const GoogleString expected = StrCat( |
| kUnrelatedNoscriptTags, |
| "<script src=\"x?a=b&c=d\" random=\"true\"" |
| " type=\"text/psajs\" orig_index=\"0\">hi1</script>", |
| kUnrelatedTags, |
| "<script src=\"y?a=b&c=d\" random=\"false\"" |
| " type=\"text/psajs\" orig_index=\"1\">hi2</script>"); |
| |
| ValidateExpectedUrl("http://example.com/", input_html, expected); |
| } |
| |
| TEST_F(JsDisableFilterTest, PrefetchScriptWithImageTemplate) { |
| SetCurrentUserAgent(UserAgentMatcherTestBase::kChrome15UserAgent); |
| SetDriverRequestHeaders(); |
| options()->set_max_prefetch_js_elements(3); |
| const GoogleString src_url = "//abc.org/m/load.php?debug=false&" |
| "lang=en&modules=startup&only=scripts&*"; |
| // Verify JS escaping works as expected and not HTML escaping. |
| const GoogleString escaped_src = "//abc.org/m/load.php?debug=false&lang=en&" |
| "modules=startup&only=scripts&*"; |
| |
| const GoogleString expected = StrCat( |
| "<head>" |
| "<script type=\"text/psajs\" orig_index=\"0\"></script>" |
| "<script src=\"blah1\" random=\"true\" type=\"text/psajs\"" |
| " orig_index=\"1\">hi1</script>" |
| "<script src=\"blah2\" random=\"false\"" |
| " type=\"text/psajs\" orig_index=\"2\">hi2</script>" |
| "<script src=\"blah3\" data-pagespeed-no-defer></script>" |
| "<script src=\"", src_url, "\" type=\"text/psajs\"" |
| " orig_index=\"3\">hi4</script>" |
| "<script src=\"blah5\" type=\"text/psajs\"" |
| " orig_index=\"4\">Not a prefetch candidate</script>" |
| "</head><body>" |
| "<script data-pagespeed-no-defer>(function(){" |
| "new Image().src=\"blah1\";" |
| "new Image().src=\"blah2\";" |
| "new Image().src=\"", escaped_src, "\";})()" |
| "</script></body>"); |
| |
| html_parse()->SetWriter(&write_to_string_); |
| html_parse()->StartParse("http://example.com"); |
| html_parse()->ParseText( |
| "<head>" |
| "<script></script>" |
| "<script src=\"blah1\" random=\"true\">hi1"); |
| html_parse()->Flush(); |
| html_parse()->ParseText(StrCat("</script>" |
| "<script src=\"blah2\" random=\"false\">hi2</script>" |
| "<script src=\"blah3\" data-pagespeed-no-defer></script>" |
| "<script src=\"", src_url, "\">hi4</script>" |
| "<script src=\"blah5\">Not a prefetch candidate</script>" |
| "</head><body>")); |
| html_parse()->Flush(); |
| html_parse()->ParseText("</body>"); |
| html_parse()->FinishParse(); |
| EXPECT_STREQ(expected, output_buffer_); |
| } |
| |
| TEST_F(JsDisableFilterTest, PrefetchScriptInHeadNotInBody) { |
| SetCurrentUserAgent(UserAgentMatcherTestBase::kChrome15UserAgent); |
| options()->set_max_prefetch_js_elements(3); |
| const GoogleString input_html = StrCat( |
| "<head>", |
| kUnrelatedNoscriptTags, |
| "<script></script>" |
| "<script src=\"blah1\" random=\"true\">hi1</script>", |
| kUnrelatedTags, |
| "<script src=\"blah2\" random=\"false\">hi2</script>" |
| "</head><body>" |
| "<script src=\"blah3\" data-pagespeed-no-defer></script>" |
| "<script src=\"blah4\">dont show up in prefetch script</script>" |
| "</body>"); |
| |
| const GoogleString prefetch_script = |
| "<script data-pagespeed-no-defer>(function(){" |
| "new Image().src=\"blah1\";" |
| "new Image().src=\"blah2\";})()" |
| "</script>"; |
| |
| const GoogleString expected = StrCat( |
| "<head>", |
| kUnrelatedNoscriptTags, |
| "<script type=\"text/psajs\" orig_index=\"0\"></script>" |
| "<script src=\"blah1\" random=\"true\" type=\"text/psajs\"" |
| " orig_index=\"1\">hi1</script>", |
| kUnrelatedTags, StrCat( |
| "<script src=\"blah2\" random=\"false\"" |
| " type=\"text/psajs\" orig_index=\"2\">hi2</script>" |
| "</head>" |
| "<body>", prefetch_script, |
| "<script src=\"blah3\" data-pagespeed-no-defer></script>" |
| "<script src=\"blah4\" type=\"text/psajs\"" |
| " orig_index=\"3\">dont show up in prefetch script</script>" |
| "</body>")); |
| |
| ValidateExpectedUrl("http://example.com/", input_html, expected); |
| } |
| |
| TEST_F(JsDisableFilterTest, DisablesScriptWithUnescapedQueryParam) { |
| const GoogleString input_html = StrCat( |
| kUnrelatedNoscriptTags, |
| "<script src=\"x?a=b&c=d\" random=\"true\">hi1</script>", |
| kUnrelatedTags, |
| "<script src=\"y?a=b&c=d\" random=\"false\">hi2</script>"); |
| const GoogleString expected = StrCat( |
| kUnrelatedNoscriptTags, |
| "<script src=\"x?a=b&c=d\" random=\"true\"" |
| " type=\"text/psajs\" orig_index=\"0\">hi1</script>", |
| kUnrelatedTags, |
| "<script src=\"y?a=b&c=d\" random=\"false\"" |
| " type=\"text/psajs\" orig_index=\"1\">hi2</script>"); |
| |
| ValidateExpectedUrl("http://example.com/", input_html, expected); |
| } |
| |
| TEST_F(JsDisableFilterTest, DisablesScriptWithNullSrc) { |
| const GoogleString input_html = StrCat( |
| kUnrelatedNoscriptTags, |
| "<script src random=\"true\">hi1</script>", |
| kUnrelatedTags, |
| "<script src random=\"false\">hi2</script>"); |
| const GoogleString expected = StrCat( |
| kUnrelatedNoscriptTags, |
| "<script src random=\"true\" type=\"text/psajs\"" |
| " orig_index=\"0\">hi1</script>", |
| kUnrelatedTags, |
| "<script src random=\"false\" type=\"text/psajs\"" |
| " orig_index=\"1\">hi2</script>"); |
| |
| ValidateExpected("http://example.com/", input_html, expected); |
| } |
| |
| TEST_F(JsDisableFilterTest, DisablesScriptOnlyFromFirstSrc) { |
| options()->set_enable_defer_js_experimental(true); |
| options_->EnableFilter(RewriteOptions::kDeferJavascript); |
| const GoogleString input_html = StrCat( |
| "<body>", |
| kUnrelatedNoscriptTags, |
| "<script random=\"true\">hi1</script>", |
| kUnrelatedTags, |
| "<script random=\"false\">hi2</script>" |
| "<script src=\"1.js?a#12296;=en\"></script></body>"); |
| const GoogleString expected = StrCat( |
| "<body>", |
| kUnrelatedNoscriptTags, |
| "<script random=\"true\" type=\"text/psajs\" orig_index=\"0\">" |
| "hi1</script>", |
| kUnrelatedTags, |
| "<script random=\"false\" type=\"text/psajs\" orig_index=\"1\">" |
| "hi2</script>" |
| "<script src=\"1.js?a#12296;=en\" type=\"text/psajs\"" |
| " orig_index=\"2\"></script>" |
| "<script type=\"text/javascript\" data-pagespeed-no-defer>", |
| JsDisableFilter::kEnableJsExperimental, |
| "</script></body>"); |
| |
| ValidateExpected("http://example.com/", input_html, expected); |
| } |
| |
| TEST_F(JsDisableFilterTest, AddsMetaTagForIE) { |
| SetCurrentUserAgent("Mozilla/5.0 ( MSIE 10.0; Trident/5.0)"); |
| const GoogleString input_html = StrCat( |
| "<body>", |
| kUnrelatedNoscriptTags, |
| "<script src=\"blah1\" random=\"true\">hi1</script>", |
| kUnrelatedTags, |
| "</body>"); |
| const GoogleString expected = StrCat( |
| StrCat("<head>", |
| kXUACompatibleMetaTag, |
| "</head>" |
| "<body>"), |
| StrCat(kUnrelatedNoscriptTags, |
| "<script src=\"blah1\" random=\"true\" type=\"text/psajs\"" |
| " orig_index=\"0\">hi1</script>", |
| kUnrelatedTags), |
| "</body>"); |
| |
| ValidateExpectedUrl("http://example.com/", input_html, expected); |
| } |
| |
| TEST_F(JsDisableFilterTest, DisablesScriptWithMultipleTypeAttributes) { |
| const GoogleString input_html = StrCat( |
| kUnrelatedNoscriptTags, |
| "<script src=\"x?a=b&c=d\" type='text/javascript' type='a' type='b'>" |
| "hi1</script>", |
| kUnrelatedTags); |
| const GoogleString expected = StrCat( |
| kUnrelatedNoscriptTags, |
| "<script src=\"x?a=b&c=d\"" |
| " data-pagespeed-orig-type='text/javascript'" |
| " type=\"text/psajs\" orig_index=\"0\">hi1</script>", |
| kUnrelatedTags); |
| |
| ValidateExpectedUrl("http://example.com/", input_html, expected); |
| } |
| |
| TEST_F(JsDisableFilterTest, ScriptWithPagespeedPrioritizeAttribute) { |
| options()->set_enable_prioritizing_scripts(true); |
| const GoogleString input_html = StrCat( |
| "<body>", |
| kUnrelatedNoscriptTags, |
| "<script src=\"blah1\" random=\"true\">hi1</script>", |
| kUnrelatedTags, |
| "<img src=\"abc.jpg\" onload=\"foo1();foo2();\">" |
| "<script src=\"blah2\" random=\"false\" data-pagespeed-prioritize>hi2" |
| "</script>" |
| "<script data-pagespeed-prioritize>hi5</script>" |
| "</body>"); |
| const GoogleString expected = StrCat( |
| "<body>", |
| kUnrelatedNoscriptTags, |
| "<script src=\"blah1\" random=\"true\" type=\"text/psajs\"" |
| " orig_index=\"0\">hi1</script>", |
| kUnrelatedTags, StrCat( |
| "<img src=\"abc.jpg\" data-pagespeed-onload=\"foo1();foo2();\" " |
| "onload=\"", JsDisableFilter::kElementOnloadCode, "\">" |
| "<script src=\"blah2\" random=\"false\" data-pagespeed-prioritize " |
| "type=\"text/prioritypsajs\" orig_index=\"1\">" |
| "hi2</script>" |
| "<script data-pagespeed-prioritize type=\"text/prioritypsajs\" " |
| "orig_index=\"2\">hi5</script>" |
| "</body>")); |
| ValidateExpectedUrl("http://example.com/", input_html, expected); |
| } |
| |
| } // namespace net_instaweb |