Fixes option-right in textfields when VoiceOver is set to read to the right of the...
[chromium-blink-merge.git] / chrome / renderer / safe_browsing / phishing_classifier_delegate_browsertest.cc
blobc947a5dfcad4a0cff4f25a5ea4908327ec80cd04
1 // Copyright (c) 2012 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 // Note: this test uses RenderViewFakeResourcesTest in order to set up a
6 // real RenderThread to hold the phishing Scorer object.
8 #include "chrome/renderer/safe_browsing/phishing_classifier_delegate.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "base/utf_string_conversions.h"
12 #include "chrome/common/safe_browsing/csd.pb.h"
13 #include "chrome/common/safe_browsing/safebrowsing_messages.h"
14 #include "chrome/renderer/safe_browsing/features.h"
15 #include "chrome/renderer/safe_browsing/phishing_classifier.h"
16 #include "chrome/renderer/safe_browsing/scorer.h"
17 #include "content/public/renderer/render_view.h"
18 #include "content/public/test/render_view_fake_resources_test.h"
19 #include "googleurl/src/gurl.h"
20 #include "testing/gmock/include/gmock/gmock.h"
21 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h"
22 #include "third_party/WebKit/Source/WebKit/chromium/public/WebHistoryItem.h"
23 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebURL.h"
24 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebURLRequest.h"
26 using ::testing::_;
27 using ::testing::InSequence;
28 using ::testing::Mock;
29 using ::testing::Pointee;
30 using ::testing::StrictMock;
32 namespace safe_browsing {
34 namespace {
35 class MockPhishingClassifier : public PhishingClassifier {
36 public:
37 explicit MockPhishingClassifier(content::RenderView* render_view)
38 : PhishingClassifier(render_view, NULL /* clock */) {}
40 virtual ~MockPhishingClassifier() {}
42 MOCK_METHOD2(BeginClassification, void(const string16*, const DoneCallback&));
43 MOCK_METHOD0(CancelPendingClassification, void());
45 private:
46 DISALLOW_COPY_AND_ASSIGN(MockPhishingClassifier);
49 class MockScorer : public Scorer {
50 public:
51 MockScorer() : Scorer() {}
52 virtual ~MockScorer() {}
54 MOCK_CONST_METHOD1(ComputeScore, double(const FeatureMap&));
56 private:
57 DISALLOW_COPY_AND_ASSIGN(MockScorer);
59 } // namespace
61 class PhishingClassifierDelegateTest
62 : public content::RenderViewFakeResourcesTest {
63 protected:
64 bool OnMessageReceived(const IPC::Message& message) {
65 bool handled = true;
66 IPC_BEGIN_MESSAGE_MAP(PhishingClassifierDelegateTest, message)
67 IPC_MESSAGE_HANDLER(SafeBrowsingHostMsg_PhishingDetectionDone,
68 OnPhishingDetectionDone)
69 IPC_MESSAGE_UNHANDLED(handled =
70 content::RenderViewFakeResourcesTest::OnMessageReceived(message))
71 IPC_END_MESSAGE_MAP()
72 return handled;
75 void OnPhishingDetectionDone(const std::string& verdict_str) {
76 scoped_ptr<ClientPhishingRequest> verdict(new ClientPhishingRequest);
77 if (verdict->ParseFromString(verdict_str) &&
78 verdict->IsInitialized()) {
79 verdict_.swap(verdict);
81 message_loop_.Quit();
84 // Runs the ClassificationDone callback, then waits for the
85 // PhishingDetectionDone IPC to arrive.
86 void RunClassificationDone(PhishingClassifierDelegate* delegate,
87 const ClientPhishingRequest& verdict) {
88 // Clear out any previous state.
89 verdict_.reset();
90 delegate->ClassificationDone(verdict);
91 message_loop_.Run();
94 void OnStartPhishingDetection(PhishingClassifierDelegate* delegate,
95 const GURL& url) {
96 delegate->OnStartPhishingDetection(url);
99 scoped_ptr<ClientPhishingRequest> verdict_;
102 TEST_F(PhishingClassifierDelegateTest, Navigation) {
103 MockPhishingClassifier* classifier =
104 new StrictMock<MockPhishingClassifier>(view());
105 PhishingClassifierDelegate* delegate =
106 PhishingClassifierDelegate::Create(view(), classifier);
107 MockScorer scorer;
108 delegate->SetPhishingScorer(&scorer);
109 ASSERT_TRUE(classifier->is_ready());
111 // Test an initial load. We expect classification to happen normally.
112 EXPECT_CALL(*classifier, CancelPendingClassification()).Times(2);
113 responses_["http://host.com/"] =
114 "<html><body><iframe src=\"http://sub1.com/\"></iframe></body></html>";
115 LoadURL("http://host.com/");
116 Mock::VerifyAndClearExpectations(classifier);
117 OnStartPhishingDetection(delegate, GURL("http://host.com/"));
118 string16 page_text = ASCIIToUTF16("dummy");
120 InSequence s;
121 EXPECT_CALL(*classifier, CancelPendingClassification());
122 EXPECT_CALL(*classifier, BeginClassification(Pointee(page_text), _));
123 delegate->PageCaptured(&page_text, false);
124 Mock::VerifyAndClearExpectations(classifier);
127 // Reloading the same page should not trigger a reclassification.
128 // However, it will cancel any pending classification since the
129 // content is being replaced.
130 EXPECT_CALL(*classifier, CancelPendingClassification()).Times(2);
131 GetMainFrame()->reload();
132 message_loop_.Run();
133 Mock::VerifyAndClearExpectations(classifier);
134 OnStartPhishingDetection(delegate, GURL("http://host.com/"));
135 page_text = ASCIIToUTF16("dummy");
136 EXPECT_CALL(*classifier, CancelPendingClassification());
137 delegate->PageCaptured(&page_text, false);
138 Mock::VerifyAndClearExpectations(classifier);
140 // Navigating in a subframe will not change the toplevel URL. However, this
141 // should cancel pending classification since the page content is changing.
142 // Currently, we do not start a new classification after subframe loads.
143 EXPECT_CALL(*classifier, CancelPendingClassification());
144 GetMainFrame()->firstChild()->loadRequest(
145 WebKit::WebURLRequest(GURL("http://sub2.com/")));
146 message_loop_.Run();
147 Mock::VerifyAndClearExpectations(classifier);
148 OnStartPhishingDetection(delegate, GURL("http://host.com/"));
149 page_text = ASCIIToUTF16("dummy");
150 EXPECT_CALL(*classifier, CancelPendingClassification());
151 delegate->PageCaptured(&page_text, false);
152 Mock::VerifyAndClearExpectations(classifier);
154 // Scrolling to an anchor works similarly to a subframe navigation, but
155 // see the TODO in PhishingClassifierDelegate::DidCommitProvisionalLoad.
156 EXPECT_CALL(*classifier, CancelPendingClassification());
157 LoadURL("http://host.com/#foo");
158 Mock::VerifyAndClearExpectations(classifier);
159 OnStartPhishingDetection(delegate, GURL("http://host.com/#foo"));
160 page_text = ASCIIToUTF16("dummy");
161 EXPECT_CALL(*classifier, CancelPendingClassification());
162 delegate->PageCaptured(&page_text, false);
163 Mock::VerifyAndClearExpectations(classifier);
165 // Now load a new toplevel page, which should trigger another classification.
166 EXPECT_CALL(*classifier, CancelPendingClassification());
167 LoadURL("http://host2.com/");
168 Mock::VerifyAndClearExpectations(classifier);
169 page_text = ASCIIToUTF16("dummy2");
170 OnStartPhishingDetection(delegate, GURL("http://host2.com/"));
172 InSequence s;
173 EXPECT_CALL(*classifier, CancelPendingClassification());
174 EXPECT_CALL(*classifier, BeginClassification(Pointee(page_text), _));
175 delegate->PageCaptured(&page_text, false);
176 Mock::VerifyAndClearExpectations(classifier);
179 // No classification should happen on back/forward navigation.
180 // Note: in practice, the browser will not send a StartPhishingDetection IPC
181 // in this case. However, we want to make sure that the delegate behaves
182 // correctly regardless.
183 WebKit::WebHistoryItem forward_item = GetMainFrame()->currentHistoryItem();
184 EXPECT_CALL(*classifier, CancelPendingClassification());
185 GoBack();
186 Mock::VerifyAndClearExpectations(classifier);
187 page_text = ASCIIToUTF16("dummy");
188 OnStartPhishingDetection(delegate, GURL("http://host.com/#foo"));
189 EXPECT_CALL(*classifier, CancelPendingClassification());
190 delegate->PageCaptured(&page_text, false);
191 Mock::VerifyAndClearExpectations(classifier);
193 EXPECT_CALL(*classifier, CancelPendingClassification());
194 GoForward(forward_item);
195 Mock::VerifyAndClearExpectations(classifier);
196 page_text = ASCIIToUTF16("dummy2");
197 OnStartPhishingDetection(delegate, GURL("http://host2.com/"));
198 EXPECT_CALL(*classifier, CancelPendingClassification());
199 delegate->PageCaptured(&page_text, false);
200 Mock::VerifyAndClearExpectations(classifier);
202 // Now go back again and scroll to a different anchor.
203 // No classification should happen.
204 EXPECT_CALL(*classifier, CancelPendingClassification());
205 GoBack();
206 Mock::VerifyAndClearExpectations(classifier);
207 page_text = ASCIIToUTF16("dummy");
208 OnStartPhishingDetection(delegate, GURL("http://host.com/#foo"));
209 EXPECT_CALL(*classifier, CancelPendingClassification());
210 delegate->PageCaptured(&page_text, false);
211 Mock::VerifyAndClearExpectations(classifier);
213 EXPECT_CALL(*classifier, CancelPendingClassification());
214 LoadURL("http://host.com/#foo2");
215 Mock::VerifyAndClearExpectations(classifier);
216 OnStartPhishingDetection(delegate, GURL("http://host.com/#foo2"));
217 page_text = ASCIIToUTF16("dummy");
218 EXPECT_CALL(*classifier, CancelPendingClassification());
219 delegate->PageCaptured(&page_text, false);
220 Mock::VerifyAndClearExpectations(classifier);
222 // The delegate will cancel pending classification on destruction.
223 EXPECT_CALL(*classifier, CancelPendingClassification());
226 TEST_F(PhishingClassifierDelegateTest, NoScorer) {
227 // For this test, we'll create the delegate with no scorer available yet.
228 MockPhishingClassifier* classifier =
229 new StrictMock<MockPhishingClassifier>(view());
230 PhishingClassifierDelegate* delegate =
231 PhishingClassifierDelegate::Create(view(), classifier);
232 ASSERT_FALSE(classifier->is_ready());
234 // Queue up a pending classification, cancel it, then queue up another one.
235 LoadURL("http://host.com/");
236 string16 page_text = ASCIIToUTF16("dummy");
237 OnStartPhishingDetection(delegate, GURL("http://host.com/"));
238 delegate->PageCaptured(&page_text, false);
240 LoadURL("http://host2.com/");
241 page_text = ASCIIToUTF16("dummy2");
242 OnStartPhishingDetection(delegate, GURL("http://host2.com/"));
243 delegate->PageCaptured(&page_text, false);
245 // Now set a scorer, which should cause a classifier to be created and
246 // the classification to proceed.
247 page_text = ASCIIToUTF16("dummy2");
248 EXPECT_CALL(*classifier, BeginClassification(Pointee(page_text), _));
249 MockScorer scorer;
250 delegate->SetPhishingScorer(&scorer);
251 Mock::VerifyAndClearExpectations(classifier);
253 // If we set a new scorer while a classification is going on the
254 // classification should be cancelled.
255 EXPECT_CALL(*classifier, CancelPendingClassification());
256 delegate->SetPhishingScorer(&scorer);
257 Mock::VerifyAndClearExpectations(classifier);
259 // The delegate will cancel pending classification on destruction.
260 EXPECT_CALL(*classifier, CancelPendingClassification());
263 TEST_F(PhishingClassifierDelegateTest, NoScorer_Ref) {
264 // Similar to the last test, but navigates within the page before
265 // setting the scorer.
266 MockPhishingClassifier* classifier =
267 new StrictMock<MockPhishingClassifier>(view());
268 PhishingClassifierDelegate* delegate =
269 PhishingClassifierDelegate::Create(view(), classifier);
270 ASSERT_FALSE(classifier->is_ready());
272 // Queue up a pending classification, cancel it, then queue up another one.
273 LoadURL("http://host.com/");
274 string16 page_text = ASCIIToUTF16("dummy");
275 OnStartPhishingDetection(delegate, GURL("http://host.com/"));
276 delegate->PageCaptured(&page_text, false);
278 LoadURL("http://host.com/#foo");
279 OnStartPhishingDetection(delegate, GURL("http://host.com/#foo"));
280 page_text = ASCIIToUTF16("dummy");
281 delegate->PageCaptured(&page_text, false);
283 // Now set a scorer, which should cause a classifier to be created and
284 // the classification to proceed.
285 page_text = ASCIIToUTF16("dummy");
286 EXPECT_CALL(*classifier, BeginClassification(Pointee(page_text), _));
287 MockScorer scorer;
288 delegate->SetPhishingScorer(&scorer);
289 Mock::VerifyAndClearExpectations(classifier);
291 // The delegate will cancel pending classification on destruction.
292 EXPECT_CALL(*classifier, CancelPendingClassification());
295 TEST_F(PhishingClassifierDelegateTest, NoStartPhishingDetection) {
296 // Tests the behavior when OnStartPhishingDetection has not yet been called
297 // when the page load finishes.
298 MockPhishingClassifier* classifier =
299 new StrictMock<MockPhishingClassifier>(view());
300 PhishingClassifierDelegate* delegate =
301 PhishingClassifierDelegate::Create(view(), classifier);
302 MockScorer scorer;
303 delegate->SetPhishingScorer(&scorer);
304 ASSERT_TRUE(classifier->is_ready());
306 EXPECT_CALL(*classifier, CancelPendingClassification());
307 responses_["http://host.com/"] = "<html><body>phish</body></html>";
308 LoadURL("http://host.com/");
309 Mock::VerifyAndClearExpectations(classifier);
310 string16 page_text = ASCIIToUTF16("phish");
311 EXPECT_CALL(*classifier, CancelPendingClassification());
312 delegate->PageCaptured(&page_text, false);
313 Mock::VerifyAndClearExpectations(classifier);
314 // Now simulate the StartPhishingDetection IPC. We expect classification
315 // to begin.
316 page_text = ASCIIToUTF16("phish");
317 EXPECT_CALL(*classifier, BeginClassification(Pointee(page_text), _));
318 OnStartPhishingDetection(delegate, GURL("http://host.com/"));
319 Mock::VerifyAndClearExpectations(classifier);
321 // Now try again, but this time we will navigate the page away before
322 // the IPC is sent.
323 EXPECT_CALL(*classifier, CancelPendingClassification());
324 responses_["http://host2.com/"] = "<html><body>phish</body></html>";
325 LoadURL("http://host2.com/");
326 Mock::VerifyAndClearExpectations(classifier);
327 page_text = ASCIIToUTF16("phish");
328 EXPECT_CALL(*classifier, CancelPendingClassification());
329 delegate->PageCaptured(&page_text, false);
330 Mock::VerifyAndClearExpectations(classifier);
332 EXPECT_CALL(*classifier, CancelPendingClassification());
333 responses_["http://host3.com/"] = "<html><body>phish</body></html>";
334 LoadURL("http://host3.com/");
335 Mock::VerifyAndClearExpectations(classifier);
336 OnStartPhishingDetection(delegate, GURL("http://host2.com/"));
338 // In this test, the original page is a redirect, which we do not get a
339 // StartPhishingDetection IPC for. We use location.replace() to load a
340 // new page while reusing the original session history entry, and check that
341 // classification begins correctly for the landing page.
342 EXPECT_CALL(*classifier, CancelPendingClassification());
343 responses_["http://host4.com/"] = "<html><body>abc</body></html>";
344 LoadURL("http://host4.com/");
345 Mock::VerifyAndClearExpectations(classifier);
346 page_text = ASCIIToUTF16("abc");
347 EXPECT_CALL(*classifier, CancelPendingClassification());
348 delegate->PageCaptured(&page_text, false);
349 Mock::VerifyAndClearExpectations(classifier);
350 responses_["http://host4.com/redir"] = "<html><body>123</body></html>";
351 EXPECT_CALL(*classifier, CancelPendingClassification());
352 LoadURL("javascript:location.replace(\'redir\');");
353 Mock::VerifyAndClearExpectations(classifier);
354 OnStartPhishingDetection(delegate, GURL("http://host4.com/redir"));
355 page_text = ASCIIToUTF16("123");
357 InSequence s;
358 EXPECT_CALL(*classifier, CancelPendingClassification());
359 EXPECT_CALL(*classifier, BeginClassification(Pointee(page_text), _));
360 delegate->PageCaptured(&page_text, false);
361 Mock::VerifyAndClearExpectations(classifier);
364 // The delegate will cancel pending classification on destruction.
365 EXPECT_CALL(*classifier, CancelPendingClassification());
368 TEST_F(PhishingClassifierDelegateTest, IgnorePreliminaryCapture) {
369 // Tests that preliminary PageCaptured notifications are ignored.
370 MockPhishingClassifier* classifier =
371 new StrictMock<MockPhishingClassifier>(view());
372 PhishingClassifierDelegate* delegate =
373 PhishingClassifierDelegate::Create(view(), classifier);
374 MockScorer scorer;
375 delegate->SetPhishingScorer(&scorer);
376 ASSERT_TRUE(classifier->is_ready());
378 EXPECT_CALL(*classifier, CancelPendingClassification());
379 responses_["http://host.com/"] = "<html><body>phish</body></html>";
380 LoadURL("http://host.com/");
381 Mock::VerifyAndClearExpectations(classifier);
382 OnStartPhishingDetection(delegate, GURL("http://host.com/"));
383 string16 page_text = ASCIIToUTF16("phish");
384 delegate->PageCaptured(&page_text, true);
386 // Once the non-preliminary capture happens, classification should begin.
387 page_text = ASCIIToUTF16("phish");
389 InSequence s;
390 EXPECT_CALL(*classifier, CancelPendingClassification());
391 EXPECT_CALL(*classifier, BeginClassification(Pointee(page_text), _));
392 delegate->PageCaptured(&page_text, false);
393 Mock::VerifyAndClearExpectations(classifier);
396 // The delegate will cancel pending classification on destruction.
397 EXPECT_CALL(*classifier, CancelPendingClassification());
400 TEST_F(PhishingClassifierDelegateTest, DuplicatePageCapture) {
401 // Tests that a second PageCaptured notification causes classification to
402 // be cancelled.
403 MockPhishingClassifier* classifier =
404 new StrictMock<MockPhishingClassifier>(view());
405 PhishingClassifierDelegate* delegate =
406 PhishingClassifierDelegate::Create(view(), classifier);
407 MockScorer scorer;
408 delegate->SetPhishingScorer(&scorer);
409 ASSERT_TRUE(classifier->is_ready());
411 EXPECT_CALL(*classifier, CancelPendingClassification());
412 responses_["http://host.com/"] = "<html><body>phish</body></html>";
413 LoadURL("http://host.com/");
414 Mock::VerifyAndClearExpectations(classifier);
415 OnStartPhishingDetection(delegate, GURL("http://host.com/"));
416 string16 page_text = ASCIIToUTF16("phish");
418 InSequence s;
419 EXPECT_CALL(*classifier, CancelPendingClassification());
420 EXPECT_CALL(*classifier, BeginClassification(Pointee(page_text), _));
421 delegate->PageCaptured(&page_text, false);
422 Mock::VerifyAndClearExpectations(classifier);
425 page_text = ASCIIToUTF16("phish");
426 EXPECT_CALL(*classifier, CancelPendingClassification());
427 delegate->PageCaptured(&page_text, false);
428 Mock::VerifyAndClearExpectations(classifier);
430 // The delegate will cancel pending classification on destruction.
431 EXPECT_CALL(*classifier, CancelPendingClassification());
434 TEST_F(PhishingClassifierDelegateTest, PhishingDetectionDone) {
435 // Tests that a PhishingDetectionDone IPC is sent to the browser
436 // whenever we finish classification.
437 MockPhishingClassifier* classifier =
438 new StrictMock<MockPhishingClassifier>(view());
439 PhishingClassifierDelegate* delegate =
440 PhishingClassifierDelegate::Create(view(), classifier);
441 MockScorer scorer;
442 delegate->SetPhishingScorer(&scorer);
443 ASSERT_TRUE(classifier->is_ready());
445 // Start by loading a page to populate the delegate's state.
446 responses_["http://host.com/"] = "<html><body>phish</body></html>";
447 EXPECT_CALL(*classifier, CancelPendingClassification());
448 LoadURL("http://host.com/#a");
449 Mock::VerifyAndClearExpectations(classifier);
450 string16 page_text = ASCIIToUTF16("phish");
451 OnStartPhishingDetection(delegate, GURL("http://host.com/#a"));
453 InSequence s;
454 EXPECT_CALL(*classifier, CancelPendingClassification());
455 EXPECT_CALL(*classifier, BeginClassification(Pointee(page_text), _));
456 delegate->PageCaptured(&page_text, false);
457 Mock::VerifyAndClearExpectations(classifier);
460 // Now run the callback to simulate the classifier finishing.
461 ClientPhishingRequest verdict;
462 verdict.set_url("http://host.com/#a");
463 verdict.set_client_score(0.8f);
464 verdict.set_is_phishing(false); // Send IPC even if site is not phishing.
465 RunClassificationDone(delegate, verdict);
466 ASSERT_TRUE(verdict_.get());
467 EXPECT_EQ(verdict.SerializeAsString(), verdict_->SerializeAsString());
469 // The delegate will cancel pending classification on destruction.
470 EXPECT_CALL(*classifier, CancelPendingClassification());
473 } // namespace safe_browsing