Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / activity_log / ad_injection_browsertest.cc
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/files/file_path.h"
6 #include "base/scoped_observer.h"
7 #include "base/strings/string_number_conversions.h"
8 #include "base/strings/string_util.h"
9 #include "base/strings/stringprintf.h"
10 #include "chrome/browser/extensions/activity_log/activity_actions.h"
11 #include "chrome/browser/extensions/activity_log/activity_log.h"
12 #include "chrome/browser/extensions/activity_log/ad_network_database.h"
13 #include "chrome/browser/extensions/extension_browsertest.h"
14 #include "chrome/test/base/ui_test_utils.h"
15 #include "extensions/common/extension.h"
16 #include "extensions/test/extension_test_message_listener.h"
17 #include "net/test/embedded_test_server/embedded_test_server.h"
18 #include "net/test/embedded_test_server/http_response.h"
19 #include "url/gurl.h"
20
21 namespace net {
22 namespace test_server {
23 struct HttpRequest;
24 }
25 }
26
27 namespace extensions {
28
29 namespace {
30
31 // The "ad network" that we are using. Any src or href equal to this should be
32 // considered an ad network.
33 const char kAdNetwork1[] = "http://www.known-ads.adnetwork";
34 const char kAdNetwork2[] = "http://www.also-known-ads.adnetwork";
35
36 // The current stage of the test.
37 enum Stage {
38   BEFORE_RESET,  // We are about to reset the page.
39   RESETTING,     // We are resetting the page.
40   TESTING        // The reset is complete, and we are testing.
41 };
42
43 // The string sent by the test to indicate that the page reset will begin.
44 const char kResetBeginString[] = "Page Reset Begin";
45 // The string sent by the test to indicate that page reset is complete.
46 const char kResetEndString[] = "Page Reset End";
47 // The string sent by the test to indicate a JS error was caught in the test.
48 const char kJavascriptErrorString[] = "Testing Error";
49 // The string sent by the test to indicate that we have concluded the full test.
50 const char kTestCompleteString[] = "Test Complete";
51
52 std::string InjectionTypeToString(Action::InjectionType type) {
53   switch (type) {
54     case Action::NO_AD_INJECTION:
55       return "No Ad Injection";
56     case Action::INJECTION_NEW_AD:
57       return "Injection New Ad";
58     case Action::INJECTION_REMOVED_AD:
59       return "Injection Removed Ad";
60     case Action::INJECTION_REPLACED_AD:
61       return "Injection Replaced Ad";
62     case Action::INJECTION_LIKELY_NEW_AD:
63       return "Injection Likely New Ad";
64     case Action::INJECTION_LIKELY_REPLACED_AD:
65       return "Injection Likely Replaced Ad";
66     case Action::NUM_INJECTION_TYPES:
67       return "Num Injection Types";
68   }
69   return std::string();
70 }
71
72 // An implementation of ActivityLog::Observer that, for every action, sends it
73 // through Action::DidInjectAd(). This will keep track of the observed
74 // injections, and can be enabled or disabled as needed (for instance, this
75 // should be disabled while we are resetting the page).
76 class ActivityLogObserver : public ActivityLog::Observer {
77  public:
78   explicit ActivityLogObserver(content::BrowserContext* context);
79   virtual ~ActivityLogObserver();
80
81   // Disable the observer (e.g., to reset the page).
82   void disable() { enabled_ = false; }
83
84   // Enable the observer, resetting the state.
85   void enable() {
86     injection_type_ = Action::NO_AD_INJECTION;
87     found_multiple_injections_ = false;
88     enabled_ = true;
89   }
90
91   Action::InjectionType injection_type() const { return injection_type_; }
92
93   bool found_multiple_injections() const { return found_multiple_injections_; }
94
95  private:
96   void OnExtensionActivity(scoped_refptr<Action> action) override;
97
98   ScopedObserver<ActivityLog, ActivityLog::Observer> scoped_observer_;
99
100   // The associated BrowserContext.
101   content::BrowserContext* context_;
102
103   // The type of the last injection.
104   Action::InjectionType injection_type_;
105
106   // Whether or not we found multiple injection types (which shouldn't happen).
107   bool found_multiple_injections_;
108
109   // Whether or not the observer is enabled.
110   bool enabled_;
111 };
112
113 ActivityLogObserver::ActivityLogObserver(content::BrowserContext* context)
114     : scoped_observer_(this),
115       context_(context),
116       injection_type_(Action::NO_AD_INJECTION),
117       found_multiple_injections_(false),
118       enabled_(false) {
119   ActivityLog::GetInstance(context_)->AddObserver(this);
120 }
121
122 ActivityLogObserver::~ActivityLogObserver() {}
123
124 void ActivityLogObserver::OnExtensionActivity(scoped_refptr<Action> action) {
125   if (!enabled_)
126     return;
127
128   Action::InjectionType type =
129       action->DidInjectAd(NULL /* no rappor service */);
130   if (type != Action::NO_AD_INJECTION) {
131     if (injection_type_ != Action::NO_AD_INJECTION)
132       found_multiple_injections_ = true;
133     injection_type_ = type;
134   }
135 }
136
137 // A mock for the AdNetworkDatabase. This simply says that the URL
138 // http://www.known-ads.adnetwork is an ad network, and nothing else is.
139 class TestAdNetworkDatabase : public AdNetworkDatabase {
140  public:
141   TestAdNetworkDatabase();
142   ~TestAdNetworkDatabase() override;
143
144  private:
145   bool IsAdNetwork(const GURL& url) const override;
146
147   GURL ad_network_url1_;
148   GURL ad_network_url2_;
149 };
150
151 TestAdNetworkDatabase::TestAdNetworkDatabase() : ad_network_url1_(kAdNetwork1),
152                                                  ad_network_url2_(kAdNetwork2) {
153 }
154
155 TestAdNetworkDatabase::~TestAdNetworkDatabase() {}
156
157 bool TestAdNetworkDatabase::IsAdNetwork(const GURL& url) const {
158   return url == ad_network_url1_ || url == ad_network_url2_;
159 }
160
161 scoped_ptr<net::test_server::HttpResponse> HandleRequest(
162     const net::test_server::HttpRequest& request) {
163   scoped_ptr<net::test_server::BasicHttpResponse> response(
164       new net::test_server::BasicHttpResponse());
165   response->set_code(net::HTTP_OK);
166   return response.Pass();
167 }
168
169 }  // namespace
170
171 class AdInjectionBrowserTest : public ExtensionBrowserTest {
172  protected:
173   AdInjectionBrowserTest();
174   ~AdInjectionBrowserTest() override;
175
176   void SetUpOnMainThread() override;
177   void TearDownOnMainThread() override;
178
179   // Handle the "Reset Begin" stage of the test.
180   testing::AssertionResult HandleResetBeginStage();
181
182   // Handle the "Reset End" stage of the test.
183   testing::AssertionResult HandleResetEndStage();
184
185   // Handle the "Testing" stage of the test.
186   testing::AssertionResult HandleTestingStage(const std::string& message);
187
188   // Handle a JS error encountered in a test.
189   testing::AssertionResult HandleJSError(const std::string& message);
190
191   const base::FilePath& test_data_dir() { return test_data_dir_; }
192
193   ExtensionTestMessageListener* listener() { return listener_.get(); }
194
195   ActivityLogObserver* observer() { return observer_.get(); }
196
197  private:
198   // The name of the last completed test; used in case of unexpected failure for
199   // debugging.
200   std::string last_test_;
201
202   // A listener for any messages from our ad-injecting extension.
203   scoped_ptr<ExtensionTestMessageListener> listener_;
204
205   // An observer to be alerted when we detect ad injection.
206   scoped_ptr<ActivityLogObserver> observer_;
207
208   // The current stage of the test.
209   Stage stage_;
210 };
211
212 AdInjectionBrowserTest::AdInjectionBrowserTest() : stage_(BEFORE_RESET) {
213 }
214
215 AdInjectionBrowserTest::~AdInjectionBrowserTest() {
216 }
217
218 void AdInjectionBrowserTest::SetUpOnMainThread() {
219   ExtensionBrowserTest::SetUpOnMainThread();
220
221   ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
222   embedded_test_server()->RegisterRequestHandler(base::Bind(&HandleRequest));
223
224   test_data_dir_ =
225       test_data_dir_.AppendASCII("activity_log").AppendASCII("ad_injection");
226   observer_.reset(new ActivityLogObserver(profile()));
227
228   // We use a listener in order to keep the actions in the Javascript test
229   // synchronous. At the end of each stage, the test will send us a message
230   // with the stage and status, and will not advance until we reply with
231   // a message.
232   listener_.reset(new ExtensionTestMessageListener(true /* will reply */));
233
234   // Enable the activity log for this test.
235   ActivityLog::GetInstance(profile())->SetWatchdogAppActiveForTesting(true);
236
237   // Set the ad network database.
238   AdNetworkDatabase::SetForTesting(
239       scoped_ptr<AdNetworkDatabase>(new TestAdNetworkDatabase));
240 }
241
242 void AdInjectionBrowserTest::TearDownOnMainThread() {
243   observer_.reset(NULL);
244   listener_.reset(NULL);
245   ActivityLog::GetInstance(profile())->SetWatchdogAppActiveForTesting(false);
246
247   ExtensionBrowserTest::TearDownOnMainThread();
248 }
249
250 testing::AssertionResult AdInjectionBrowserTest::HandleResetBeginStage() {
251   if (stage_ != BEFORE_RESET) {
252     return testing::AssertionFailure()
253            << "In incorrect stage. Last Test: " << last_test_;
254   }
255
256   // Stop looking for ad injection, since some of the reset could be considered
257   // ad injection.
258   observer()->disable();
259   stage_ = RESETTING;
260   return testing::AssertionSuccess();
261 }
262
263 testing::AssertionResult AdInjectionBrowserTest::HandleResetEndStage() {
264   if (stage_ != RESETTING) {
265     return testing::AssertionFailure()
266            << "In incorrect stage. Last test: " << last_test_;
267   }
268
269   // Look for ad injection again, now that the reset is over.
270   observer()->enable();
271   stage_ = TESTING;
272   return testing::AssertionSuccess();
273 }
274
275 testing::AssertionResult AdInjectionBrowserTest::HandleTestingStage(
276     const std::string& message) {
277   if (stage_ != TESTING) {
278     return testing::AssertionFailure()
279            << "In incorrect stage. Last test: " << last_test_;
280   }
281
282   // The format for a testing message is:
283   // "<test_name>:<expected_change>"
284   // where <test_name> is the name of the test and <expected_change> is
285   // either -1 for no ad injection (to test against false positives) or the
286   // number corresponding to ad_detection::InjectionType.
287   size_t sep = message.find(':');
288   int expected_change = -1;
289   if (sep == std::string::npos ||
290       !base::StringToInt(message.substr(sep + 1), &expected_change) ||
291       (expected_change < Action::NO_AD_INJECTION ||
292        expected_change >= Action::NUM_INJECTION_TYPES)) {
293     return testing::AssertionFailure()
294            << "Invalid message received for testing stage: " << message;
295   }
296
297   last_test_ = message.substr(0, sep);
298
299   Action::InjectionType expected_injection =
300       static_cast<Action::InjectionType>(expected_change);
301   std::string error;
302   if (observer()->found_multiple_injections()) {
303     error = "Found multiple injection types. "
304             "Only one injection is expected per test.";
305   } else if (expected_injection != observer()->injection_type()) {
306     // We need these static casts, because size_t is different on different
307     // architectures, and printf becomes unhappy.
308     error = base::StringPrintf(
309         "Incorrect Injection Found: Expected: %s, Actual: %s",
310         InjectionTypeToString(expected_injection).c_str(),
311         InjectionTypeToString(observer()->injection_type()).c_str());
312   }
313
314   stage_ = BEFORE_RESET;
315
316   if (!error.empty()) {
317     return testing::AssertionFailure()
318         << "Error in Test '" << last_test_ << "': " << error;
319   }
320
321   return testing::AssertionSuccess();
322 }
323
324 testing::AssertionResult AdInjectionBrowserTest::HandleJSError(
325     const std::string& message) {
326   // The format for a testing message is:
327   // "Testing Error:<test_name>:<error>"
328   // where <test_name> is the name of the test and <error> is the error which
329   // was encountered.
330   size_t first_sep = message.find(':');
331   size_t second_sep = message.find(':', first_sep + 1);
332   if (first_sep == std::string::npos || second_sep == std::string::npos) {
333     return testing::AssertionFailure()
334            << "Invalid message received: " << message;
335   }
336
337   std::string test_name =
338       message.substr(first_sep + 1, second_sep - first_sep - 1);
339   std::string test_err = message.substr(second_sep + 1);
340
341   // We set the stage here, so that subsequent tests don't fail.
342   stage_ = BEFORE_RESET;
343
344   return testing::AssertionFailure() << "Javascript Error in test '"
345                                      << test_name << "': " << test_err;
346 }
347
348 // This is the primary Ad-Injection browser test. It loads an extension that
349 // has a content script that, in turn, injects ads left, right, and center.
350 // The content script waits after each injection for a response from this
351 // browsertest, in order to ensure synchronicity. After each injection, the
352 // content script cleans up after itself. For significantly more detailed
353 // comments, see
354 // chrome/test/data/extensions/activity_log/ad_injection/content_script.js.
355 IN_PROC_BROWSER_TEST_F(AdInjectionBrowserTest, DetectAdInjections) {
356   const Extension* extension = LoadExtension(test_data_dir_);
357   ASSERT_TRUE(extension);
358
359   ui_test_utils::NavigateToURL(browser(), embedded_test_server()->GetURL("/"));
360
361   std::string message;
362   while (message != "TestComplete") {
363     listener()->WaitUntilSatisfied();
364     message = listener()->message();
365     if (message == kResetBeginString) {
366       ASSERT_TRUE(HandleResetBeginStage());
367     } else if (message == kResetEndString) {
368       ASSERT_TRUE(HandleResetEndStage());
369     } else if (!message.compare(
370                    0, strlen(kJavascriptErrorString), kJavascriptErrorString)) {
371       EXPECT_TRUE(HandleJSError(message));
372     } else if (message == kTestCompleteString) {
373       break;  // We're done!
374     } else {  // We're in some kind of test.
375       EXPECT_TRUE(HandleTestingStage(message));
376     }
377
378     // In all cases (except for "Test Complete", in which case we already
379     // break'ed), we reply with a continue message.
380     listener()->Reply("Continue");
381     listener()->Reset();
382   }
383 }
384
385 // If this test grows, we should consolidate it and AdInjectionBrowserTest.
386 class ExecuteScriptAdInjectionBrowserTest : public ExtensionBrowserTest {
387  protected:
388   void SetUpOnMainThread() override;
389   void TearDownOnMainThread() override;
390 };
391
392 void ExecuteScriptAdInjectionBrowserTest::SetUpOnMainThread() {
393   ExtensionBrowserTest::SetUpOnMainThread();
394
395   ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
396   embedded_test_server()->RegisterRequestHandler(base::Bind(&HandleRequest));
397
398   // Enable the activity log for this test.
399   ActivityLog::GetInstance(profile())->SetWatchdogAppActiveForTesting(true);
400
401   // Set the ad network database.
402   AdNetworkDatabase::SetForTesting(
403       scoped_ptr<AdNetworkDatabase>(new TestAdNetworkDatabase));
404 }
405
406 void ExecuteScriptAdInjectionBrowserTest::TearDownOnMainThread() {
407   ActivityLog::GetInstance(profile())->SetWatchdogAppActiveForTesting(false);
408   ExtensionBrowserTest::TearDownOnMainThread();
409 }
410
411 // Test that using chrome.tabs.executeScript doesn't circumvent our detection.
412 // Since each type of injection is tested more thoroughly in the test above,
413 // this test just needs to make sure that we detect anything from executeScript.
414 IN_PROC_BROWSER_TEST_F(ExecuteScriptAdInjectionBrowserTest,
415                        ExecuteScriptAdInjection) {
416   const Extension* extension =
417       LoadExtension(test_data_dir_.AppendASCII("activity_log")
418                                   .AppendASCII("execute_script_ad_injection"));
419   ASSERT_TRUE(extension);
420
421   ExtensionTestMessageListener listener(false);  // Won't reply.
422   listener.set_extension_id(extension->id());
423   ActivityLogObserver observer(profile());
424   observer.enable();
425
426   ui_test_utils::NavigateToURL(browser(), embedded_test_server()->GetURL("/"));
427
428   // The extension sends a "Done" message when the script has executed.
429   listener.WaitUntilSatisfied();
430   EXPECT_EQ("Done", listener.message());
431
432   // We should have injected an ad.
433   EXPECT_EQ(Action::INJECTION_NEW_AD, observer.injection_type());
434   EXPECT_FALSE(observer.found_multiple_injections());
435 }
436
437 // TODO(rdevlin.cronin): We test a good amount of ways of injecting ads with
438 // the above test, but more is better in testing.
439 // See crbug.com/357204.
440
441 }  // namespace extensions