1 // Copyright 2016 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 "chrome/browser/ui/views/page_info/page_info_bubble_view.h"
6 
7 #include "base/run_loop.h"
8 #include "base/scoped_observer.h"
9 #include "base/test/metrics/histogram_tester.h"
10 #include "build/build_config.h"
11 #include "chrome/browser/safe_browsing/chrome_password_protection_service.h"
12 #include "chrome/browser/ssl/security_state_tab_helper.h"
13 #include "chrome/browser/ui/browser_commands.h"
14 #include "chrome/browser/ui/browser_finder.h"
15 #include "chrome/browser/ui/page_info/page_info_dialog.h"
16 #include "chrome/browser/ui/tabs/tab_strip_model.h"
17 #include "chrome/browser/ui/test/test_browser_dialog.h"
18 #include "chrome/browser/ui/view_ids.h"
19 #include "chrome/browser/ui/views/frame/browser_view.h"
20 #include "chrome/browser/ui/views/location_bar/location_bar_view.h"
21 #include "chrome/browser/ui/views/location_bar/location_icon_view.h"
22 #include "chrome/browser/ui/views/toolbar/toolbar_view.h"
23 #include "chrome/common/chrome_features.h"
24 #include "chrome/common/url_constants.h"
25 #include "chrome/test/base/in_process_browser_test.h"
26 #include "chrome/test/base/ui_test_utils.h"
27 #include "components/content_settings/core/browser/content_settings_registry.h"
28 #include "components/content_settings/core/common/content_settings_types.h"
29 #include "components/page_info/page_info.h"
30 #include "components/password_manager/core/browser/password_manager_metrics_util.h"
31 #include "components/safe_browsing/content/password_protection/metrics_util.h"
32 #include "components/safe_browsing/core/features.h"
33 #include "components/strings/grit/components_strings.h"
34 #include "content/public/browser/navigation_handle.h"
35 #include "content/public/browser/render_frame_host.h"
36 #include "content/public/browser/web_contents.h"
37 #include "content/public/browser/web_contents_observer.h"
38 #include "content/public/common/referrer.h"
39 #include "content/public/test/browser_test.h"
40 #include "content/public/test/browser_test_utils.h"
41 #include "content/public/test/test_navigation_observer.h"
42 #include "net/test/cert_test_util.h"
43 #include "net/test/embedded_test_server/http_request.h"
44 #include "net/test/embedded_test_server/http_response.h"
45 #include "net/test/test_certificate_data.h"
46 #include "net/test/test_data_directory.h"
47 #include "testing/gmock/include/gmock/gmock.h"
48 #include "testing/gtest/include/gtest/gtest.h"
49 #include "third_party/blink/public/common/features.h"
50 #include "ui/accessibility/ax_action_data.h"
51 #include "ui/accessibility/ax_enums.mojom.h"
52 #include "ui/base/l10n/l10n_util.h"
53 #include "ui/events/types/event_type.h"
54 #include "ui/views/test/widget_test.h"
55 
56 namespace {
57 
58 using password_manager::metrics_util::PasswordType;
59 
60 constexpr char kExpiredCertificateFile[] = "expired_cert.pem";
61 
62 class ClickEvent : public ui::Event {
63  public:
ClickEvent()64   ClickEvent() : ui::Event(ui::ET_UNKNOWN, base::TimeTicks(), 0) {}
65 };
66 
PerformMouseClickOnView(views::View * view)67 void PerformMouseClickOnView(views::View* view) {
68   ui::AXActionData data;
69   data.action = ax::mojom::Action::kDoDefault;
70   view->HandleAccessibleAction(data);
71 }
72 
73 // Clicks the location icon to open the page info bubble.
OpenPageInfoBubble(Browser * browser)74 void OpenPageInfoBubble(Browser* browser) {
75   BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser);
76   LocationIconView* location_icon_view =
77       browser_view->toolbar()->location_bar()->location_icon_view();
78   ASSERT_TRUE(location_icon_view);
79   ClickEvent event;
80   location_icon_view->ShowBubble(event);
81   views::BubbleDialogDelegateView* page_info =
82       PageInfoBubbleView::GetPageInfoBubbleForTesting();
83   EXPECT_NE(nullptr, page_info);
84   page_info->set_close_on_deactivate(false);
85 }
86 
87 // Opens the Page Info bubble and retrieves the UI view identified by
88 // |view_id|.
GetView(Browser * browser,int view_id)89 views::View* GetView(Browser* browser, int view_id) {
90   views::Widget* page_info_bubble =
91       PageInfoBubbleView::GetPageInfoBubbleForTesting()->GetWidget();
92   EXPECT_TRUE(page_info_bubble);
93 
94   views::View* view = page_info_bubble->GetRootView()->GetViewByID(view_id);
95   EXPECT_TRUE(view);
96   return view;
97 }
98 
99 // Clicks the "Site settings" button from Page Info and waits for a "Settings"
100 // tab to open.
ClickAndWaitForSettingsPageToOpen(views::View * site_settings_button)101 void ClickAndWaitForSettingsPageToOpen(views::View* site_settings_button) {
102   content::WebContentsAddedObserver new_tab_observer;
103   PerformMouseClickOnView(site_settings_button);
104 
105   base::string16 expected_title(base::ASCIIToUTF16("Settings"));
106   content::TitleWatcher title_watcher(new_tab_observer.GetWebContents(),
107                                       expected_title);
108   EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
109 }
110 
111 // Returns the URL of the new tab that's opened on clicking the "Site settings"
112 // button from Page Info.
OpenSiteSettingsForUrl(Browser * browser,const GURL & url)113 const GURL OpenSiteSettingsForUrl(Browser* browser, const GURL& url) {
114   ui_test_utils::NavigateToURL(browser, url);
115   OpenPageInfoBubble(browser);
116   // Get site settings button.
117   views::View* site_settings_button = GetView(
118       browser,
119       PageInfoBubbleView::VIEW_ID_PAGE_INFO_LINK_OR_BUTTON_SITE_SETTINGS);
120   ClickAndWaitForSettingsPageToOpen(site_settings_button);
121 
122   return browser->tab_strip_model()
123       ->GetActiveWebContents()
124       ->GetLastCommittedURL();
125 }
126 
127 }  // namespace
128 
129 class PageInfoBubbleViewBrowserTest : public DialogBrowserTest {
130  public:
131   PageInfoBubbleViewBrowserTest() = default;
132   // DialogBrowserTest:
ShowUi(const std::string & name)133   void ShowUi(const std::string& name) override {
134     // Bubble dialogs' bounds may exceed the display's work area.
135     // https://crbug.com/893292.
136     set_should_verify_dialog_bounds(false);
137 
138     // All the possible test names.
139     constexpr char kInsecure[] = "Insecure";
140     constexpr char kInternal[] = "Internal";
141     constexpr char kInternalExtension[] = "InternalExtension";
142     constexpr char kInternalViewSource[] = "InternalViewSource";
143     constexpr char kFile[] = "File";
144     constexpr char kSecure[] = "Secure";
145     constexpr char kEvSecure[] = "EvSecure";
146     constexpr char kMalware[] = "Malware";
147     constexpr char kDeceptive[] = "Deceptive";
148     constexpr char kUnwantedSoftware[] = "UnwantedSoftware";
149     constexpr char kSignInSyncPasswordReuse[] = "SignInSyncPasswordReuse";
150     constexpr char kSignInNonSyncPasswordReuse[] = "SignInNonSyncPasswordReuse";
151     constexpr char kEnterprisePasswordReuse[] = "EnterprisePasswordReuse";
152     constexpr char kSavedPasswordReuse[] = "SavedPasswordReuse";
153     constexpr char kMalwareAndBadCert[] = "MalwareAndBadCert";
154     constexpr char kMixedContentForm[] = "MixedContentForm";
155     constexpr char kMixedContent[] = "MixedContent";
156     constexpr char kAllowAllPermissions[] = "AllowAllPermissions";
157     constexpr char kBlockAllPermissions[] = "BlockAllPermissions";
158 
159     const GURL internal_url("chrome://settings");
160     const GURL internal_extension_url("chrome-extension://example");
161     const GURL file_url("file:///Users/homedirname/folder/file.pdf");
162     // Note the following two URLs are not really necessary to get the different
163     // versions of Page Info to appear, but are here to indicate the type of
164     // URL each IdentityInfo type would normally be associated with.
165     const GURL https_url("https://example.com");
166     const GURL http_url("http://example.com");
167 
168     GURL url = http_url;
169     if (name == kSecure || name == kEvSecure || name == kMixedContentForm ||
170         name == kMixedContent || name == kAllowAllPermissions ||
171         name == kBlockAllPermissions || name == kMalwareAndBadCert) {
172       url = https_url;
173     }
174     if (name == kInternal) {
175       url = internal_url;
176     } else if (name == kInternalExtension) {
177       url = internal_extension_url;
178     } else if (name == kInternalViewSource) {
179       constexpr char kTestHtml[] = "/viewsource/test.html";
180       ASSERT_TRUE(embedded_test_server()->Start());
181       url = GURL(content::kViewSourceScheme + std::string(":") +
182                  embedded_test_server()->GetURL(kTestHtml).spec());
183     } else if (name == kFile) {
184       url = file_url;
185     }
186 
187     ui_test_utils::NavigateToURL(browser(), url);
188     OpenPageInfoBubble(browser());
189 
190     PageInfoUI::IdentityInfo identity;
191     if (name == kInsecure) {
192       identity.identity_status = PageInfo::SITE_IDENTITY_STATUS_NO_CERT;
193     } else if (name == kSecure || name == kAllowAllPermissions ||
194                name == kBlockAllPermissions) {
195       // Generate a valid mock HTTPS identity, with a certificate.
196       identity.identity_status = PageInfo::SITE_IDENTITY_STATUS_CERT;
197       constexpr char kGoodCertificateFile[] = "ok_cert.pem";
198       identity.certificate = net::ImportCertFromFile(
199           net::GetTestCertsDirectory(), kGoodCertificateFile);
200     } else if (name == kEvSecure) {
201       // Generate a valid mock EV HTTPS identity, with an EV certificate. Must
202       // match conditions in PageInfoBubbleView::SetIdentityInfo() for setting
203       // the certificate button subtitle.
204       identity.identity_status = PageInfo::SITE_IDENTITY_STATUS_EV_CERT;
205       identity.connection_status = PageInfo::SITE_CONNECTION_STATUS_ENCRYPTED;
206       scoped_refptr<net::X509Certificate> ev_cert =
207           net::X509Certificate::CreateFromBytes(
208               reinterpret_cast<const char*>(thawte_der), sizeof(thawte_der));
209       ASSERT_TRUE(ev_cert);
210       identity.certificate = ev_cert;
211     } else if (name == kMalware) {
212       identity.safe_browsing_status = PageInfo::SAFE_BROWSING_STATUS_MALWARE;
213     } else if (name == kDeceptive) {
214       identity.safe_browsing_status =
215           PageInfo::SAFE_BROWSING_STATUS_SOCIAL_ENGINEERING;
216     } else if (name == kUnwantedSoftware) {
217       identity.safe_browsing_status =
218           PageInfo::SAFE_BROWSING_STATUS_UNWANTED_SOFTWARE;
219     } else if (name == kSignInSyncPasswordReuse) {
220       identity.safe_browsing_status =
221           PageInfo::SAFE_BROWSING_STATUS_SIGNED_IN_SYNC_PASSWORD_REUSE;
222     } else if (name == kSignInNonSyncPasswordReuse) {
223       identity.safe_browsing_status =
224           PageInfo::SAFE_BROWSING_STATUS_SIGNED_IN_NON_SYNC_PASSWORD_REUSE;
225     } else if (name == kEnterprisePasswordReuse) {
226       identity.safe_browsing_status =
227           PageInfo::SAFE_BROWSING_STATUS_ENTERPRISE_PASSWORD_REUSE;
228     } else if (name == kSavedPasswordReuse) {
229       identity.safe_browsing_status =
230           PageInfo::SAFE_BROWSING_STATUS_SAVED_PASSWORD_REUSE;
231     } else if (name == kMalwareAndBadCert) {
232       identity.identity_status = PageInfo::SITE_IDENTITY_STATUS_ERROR;
233       identity.certificate = net::ImportCertFromFile(
234           net::GetTestCertsDirectory(), kExpiredCertificateFile);
235       identity.safe_browsing_status = PageInfo::SAFE_BROWSING_STATUS_MALWARE;
236     } else if (name == kMixedContentForm) {
237       identity.identity_status =
238           PageInfo::SITE_IDENTITY_STATUS_ADMIN_PROVIDED_CERT;
239       identity.connection_status =
240           PageInfo::SITE_CONNECTION_STATUS_INSECURE_FORM_ACTION;
241     } else if (name == kMixedContent) {
242       identity.identity_status =
243           PageInfo::SITE_IDENTITY_STATUS_ADMIN_PROVIDED_CERT;
244       identity.connection_status =
245           PageInfo::SITE_CONNECTION_STATUS_INSECURE_PASSIVE_SUBRESOURCE;
246     }
247 
248     if (name == kAllowAllPermissions || name == kBlockAllPermissions) {
249       // Generate a |PermissionInfoList| with every permission allowed/blocked.
250       PermissionInfoList permissions_list;
251       for (ContentSettingsType content_type :
252            PageInfo::GetAllPermissionsForTesting()) {
253         PageInfo::PermissionInfo info;
254         info.type = content_type;
255         info.setting = (name == kAllowAllPermissions) ? CONTENT_SETTING_ALLOW
256                                                       : CONTENT_SETTING_BLOCK;
257         info.default_setting =
258             content_settings::ContentSettingsRegistry::GetInstance()
259                 ->Get(info.type)
260                 ->GetInitialDefaultSetting();
261         info.source = content_settings::SettingSource::SETTING_SOURCE_USER;
262         info.is_incognito = false;
263         permissions_list.push_back(info);
264       }
265 
266       ChosenObjectInfoList chosen_object_list;
267 
268       PageInfoBubbleView* page_info_bubble_view =
269           static_cast<PageInfoBubbleView*>(
270               PageInfoBubbleView::GetPageInfoBubbleForTesting());
271       // Normally |PageInfoBubbleView| doesn't update the permissions already
272       // shown if they change while it's still open. For this test, manually
273       // force an update by clearing the existing permission views here.
274       page_info_bubble_view->GetFocusManager()->SetFocusedView(nullptr);
275       page_info_bubble_view->selector_rows_.clear();
276       page_info_bubble_view->permissions_view_->RemoveAllChildViews(true);
277 
278       page_info_bubble_view->SetPermissionInfo(permissions_list,
279                                                std::move(chosen_object_list));
280     }
281 
282     if (name != kInsecure && name.find(kInternal) == std::string::npos &&
283         name != kFile) {
284       // The bubble may be PageInfoBubbleView or InternalPageInfoBubbleView. The
285       // latter is only used for |kInternal|, so it is safe to static_cast here.
286       static_cast<PageInfoBubbleView*>(
287           PageInfoBubbleView::GetPageInfoBubbleForTesting())
288           ->SetIdentityInfo(identity);
289     }
290   }
291 
VerifyUi()292   bool VerifyUi() override {
293     if (!DialogBrowserTest::VerifyUi())
294       return false;
295     // Check that each expected View is present in the Page Info bubble.
296     views::View* page_info_bubble_view =
297         PageInfoBubbleView::GetPageInfoBubbleForTesting()->GetContentsView();
298     for (auto id : expected_identifiers_) {
299       views::View* view = GetView(browser(), id);
300       if (!page_info_bubble_view->Contains(view))
301         return false;
302     }
303     return true;
304   }
305 
306  protected:
GetSimplePageUrl() const307   GURL GetSimplePageUrl() const {
308     return ui_test_utils::GetTestUrl(
309         base::FilePath(base::FilePath::kCurrentDirectory),
310         base::FilePath(FILE_PATH_LITERAL("simple.html")));
311   }
312 
GetIframePageUrl() const313   GURL GetIframePageUrl() const {
314     return ui_test_utils::GetTestUrl(
315         base::FilePath(base::FilePath::kCurrentDirectory),
316         base::FilePath(FILE_PATH_LITERAL("iframe_blank.html")));
317   }
318 
ExecuteJavaScriptForTests(const std::string & js)319   void ExecuteJavaScriptForTests(const std::string& js) {
320     base::RunLoop run_loop;
321     browser()
322         ->tab_strip_model()
323         ->GetActiveWebContents()
324         ->GetMainFrame()
325         ->ExecuteJavaScriptForTests(
326             base::ASCIIToUTF16(js),
327             base::BindOnce([](const base::Closure& quit_callback,
328                               base::Value result) { quit_callback.Run(); },
329                            run_loop.QuitClosure()));
330     run_loop.Run();
331   }
332 
TriggerReloadPromptOnClose() const333   void TriggerReloadPromptOnClose() const {
334     PageInfoBubbleView* const page_info_bubble_view =
335         static_cast<PageInfoBubbleView*>(
336             PageInfoBubbleView::GetPageInfoBubbleForTesting());
337     ASSERT_NE(nullptr, page_info_bubble_view);
338 
339     // Set some dummy non-default permissions. This will trigger a reload prompt
340     // when the bubble is closed.
341     PageInfo::PermissionInfo permission;
342     permission.type = ContentSettingsType::NOTIFICATIONS;
343     permission.setting = ContentSetting::CONTENT_SETTING_BLOCK;
344     permission.default_setting = ContentSetting::CONTENT_SETTING_ASK;
345     permission.source = content_settings::SettingSource::SETTING_SOURCE_USER;
346     permission.is_incognito = false;
347     page_info_bubble_view->OnPermissionChanged(permission);
348   }
349 
SetPageInfoBubbleIdentityInfo(const PageInfoUI::IdentityInfo & identity_info)350   void SetPageInfoBubbleIdentityInfo(
351       const PageInfoUI::IdentityInfo& identity_info) {
352     static_cast<PageInfoBubbleView*>(
353         PageInfoBubbleView::GetPageInfoBubbleForTesting())
354         ->SetIdentityInfo(identity_info);
355   }
356 
GetCertificateButtonTitle() const357   base::string16 GetCertificateButtonTitle() const {
358     // Only PageInfoBubbleViewBrowserTest can access certificate_button_ in
359     // PageInfoBubbleView, or title() in HoverButton.
360     PageInfoBubbleView* page_info_bubble_view =
361         static_cast<PageInfoBubbleView*>(
362             PageInfoBubbleView::GetPageInfoBubbleForTesting());
363     return page_info_bubble_view->certificate_button_->title()->GetText();
364   }
365 
GetCertificateButtonSubtitle() const366   base::string16 GetCertificateButtonSubtitle() const {
367     PageInfoBubbleView* page_info_bubble_view =
368         static_cast<PageInfoBubbleView*>(
369             PageInfoBubbleView::GetPageInfoBubbleForTesting());
370     return page_info_bubble_view->certificate_button_->subtitle()->GetText();
371   }
372 
GetPageInfoBubbleViewDetailText()373   const base::string16 GetPageInfoBubbleViewDetailText() {
374     PageInfoBubbleView* page_info_bubble_view =
375         static_cast<PageInfoBubbleView*>(
376             PageInfoBubbleView::GetPageInfoBubbleForTesting());
377     return page_info_bubble_view->details_text();
378   }
379 
380  private:
381   std::vector<PageInfoBubbleView::PageInfoBubbleViewID> expected_identifiers_;
382 
383   DISALLOW_COPY_AND_ASSIGN(PageInfoBubbleViewBrowserTest);
384 };
385 
IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest,ShowBubble)386 IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest, ShowBubble) {
387   OpenPageInfoBubble(browser());
388   EXPECT_EQ(PageInfoBubbleView::BUBBLE_PAGE_INFO,
389             PageInfoBubbleView::GetShownBubbleType());
390 }
391 
IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest,ChromeURL)392 IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest, ChromeURL) {
393   ui_test_utils::NavigateToURL(browser(), GURL("chrome://settings"));
394   OpenPageInfoBubble(browser());
395   EXPECT_EQ(PageInfoBubbleView::BUBBLE_INTERNAL_PAGE,
396             PageInfoBubbleView::GetShownBubbleType());
397 }
398 
IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest,ChromeExtensionURL)399 IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest, ChromeExtensionURL) {
400   ui_test_utils::NavigateToURL(
401       browser(), GURL("chrome-extension://extension-id/options.html"));
402   OpenPageInfoBubble(browser());
403   EXPECT_EQ(PageInfoBubbleView::BUBBLE_INTERNAL_PAGE,
404             PageInfoBubbleView::GetShownBubbleType());
405 }
406 
IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest,ChromeDevtoolsURL)407 IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest, ChromeDevtoolsURL) {
408   ui_test_utils::NavigateToURL(
409       browser(), GURL("devtools://devtools/bundled/inspector.html"));
410   OpenPageInfoBubble(browser());
411   EXPECT_EQ(PageInfoBubbleView::BUBBLE_INTERNAL_PAGE,
412             PageInfoBubbleView::GetShownBubbleType());
413 }
414 
IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest,ViewSourceURL)415 IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest, ViewSourceURL) {
416   ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
417   browser()
418       ->tab_strip_model()
419       ->GetActiveWebContents()
420       ->GetMainFrame()
421       ->ViewSource();
422   OpenPageInfoBubble(browser());
423   EXPECT_EQ(PageInfoBubbleView::BUBBLE_INTERNAL_PAGE,
424             PageInfoBubbleView::GetShownBubbleType());
425 }
426 
427 // Test opening "Site Details" via Page Info from an ASCII origin does the
428 // correct URL canonicalization.
IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest,SiteSettingsLink)429 IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest, SiteSettingsLink) {
430   GURL url = GURL("https://www.google.com/");
431   std::string expected_origin = "https%3A%2F%2Fwww.google.com";
432   EXPECT_EQ(GURL(chrome::kChromeUISiteDetailsPrefixURL + expected_origin),
433             OpenSiteSettingsForUrl(browser(), url));
434 }
435 
436 // Test opening "Site Details" via Page Info from a non-ASCII URL converts it to
437 // an origin and does punycode conversion as well as URL canonicalization.
IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest,SiteSettingsLinkWithNonAsciiUrl)438 IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest,
439                        SiteSettingsLinkWithNonAsciiUrl) {
440   GURL url = GURL("http://��.ws/other/stuff.htm");
441   std::string expected_origin = "http%3A%2F%2Fxn--9q9h.ws";
442   EXPECT_EQ(GURL(chrome::kChromeUISiteDetailsPrefixURL + expected_origin),
443             OpenSiteSettingsForUrl(browser(), url));
444 }
445 
446 // Test opening "Site Details" via Page Info from an origin with a non-default
447 // (scheme, port) pair will specify port # in the origin passed to query params.
IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest,SiteSettingsLinkWithNonDefaultPort)448 IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest,
449                        SiteSettingsLinkWithNonDefaultPort) {
450   GURL url = GURL("https://www.example.com:8372");
451   std::string expected_origin = "https%3A%2F%2Fwww.example.com%3A8372";
452   EXPECT_EQ(GURL(chrome::kChromeUISiteDetailsPrefixURL + expected_origin),
453             OpenSiteSettingsForUrl(browser(), url));
454 }
455 
456 // Test opening "Site Details" via Page Info from about:blank goes to "Content
457 // Settings" (the alternative is a blank origin being sent to "Site Details").
IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest,SiteSettingsLinkWithAboutBlankURL)458 IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest,
459                        SiteSettingsLinkWithAboutBlankURL) {
460   EXPECT_EQ(GURL(chrome::kChromeUIContentSettingsURL),
461             OpenSiteSettingsForUrl(browser(), GURL(url::kAboutBlankURL)));
462 }
463 
464 // Test opening page info bubble that matches
465 // SB_THREAT_TYPE_ENTERPRISE_PASSWORD_REUSE threat type.
IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest,VerifyEnterprisePasswordReusePageInfoBubble)466 IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest,
467                        VerifyEnterprisePasswordReusePageInfoBubble) {
468   ASSERT_TRUE(embedded_test_server()->Start());
469   base::HistogramTester histograms;
470   ui_test_utils::NavigateToURL(browser(), embedded_test_server()->GetURL("/"));
471 
472   // Update security state of the current page to match
473   // SB_THREAT_TYPE_ENTERPRISE_PASSWORD_REUSE.
474   safe_browsing::ChromePasswordProtectionService* service =
475       safe_browsing::ChromePasswordProtectionService::
476           GetPasswordProtectionService(browser()->profile());
477   content::WebContents* contents =
478       browser()->tab_strip_model()->GetActiveWebContents();
479   safe_browsing::ReusedPasswordAccountType reused_password_account_type;
480   reused_password_account_type.set_account_type(
481       safe_browsing::ReusedPasswordAccountType::NON_GAIA_ENTERPRISE);
482   service->set_reused_password_account_type_for_last_shown_warning(
483       reused_password_account_type);
484 
485   service->ShowModalWarning(
486       contents, safe_browsing::RequestOutcome::UNKNOWN,
487       safe_browsing::LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
488       "unused_token", reused_password_account_type);
489 
490   OpenPageInfoBubble(browser());
491   views::View* change_password_button = GetView(
492       browser(), PageInfoBubbleView::VIEW_ID_PAGE_INFO_BUTTON_CHANGE_PASSWORD);
493   views::View* allowlist_password_reuse_button = GetView(
494       browser(),
495       PageInfoBubbleView::VIEW_ID_PAGE_INFO_BUTTON_ALLOWLIST_PASSWORD_REUSE);
496 
497   SecurityStateTabHelper* helper =
498       SecurityStateTabHelper::FromWebContents(contents);
499   std::unique_ptr<security_state::VisibleSecurityState> visible_security_state =
500       helper->GetVisibleSecurityState();
501   ASSERT_EQ(security_state::MALICIOUS_CONTENT_STATUS_ENTERPRISE_PASSWORD_REUSE,
502             visible_security_state->malicious_content_status);
503   ASSERT_EQ(l10n_util::GetStringUTF16(
504                 IDS_PAGE_INFO_CHANGE_PASSWORD_DETAILS_ENTERPRISE),
505             GetPageInfoBubbleViewDetailText());
506 
507   // Verify these two buttons are showing.
508   EXPECT_TRUE(change_password_button->GetVisible());
509   EXPECT_TRUE(allowlist_password_reuse_button->GetVisible());
510 
511   // Verify clicking on button will increment corresponding bucket of
512   // PasswordProtection.PageInfoAction.NonGaiaEnterprisePasswordEntry histogram.
513   PerformMouseClickOnView(change_password_button);
514   EXPECT_THAT(
515       histograms.GetAllSamples(
516           safe_browsing::kEnterprisePasswordPageInfoHistogram),
517       testing::ElementsAre(
518           base::Bucket(static_cast<int>(safe_browsing::WarningAction::SHOWN),
519                        1),
520           base::Bucket(
521               static_cast<int>(safe_browsing::WarningAction::CHANGE_PASSWORD),
522               1)));
523 
524   PerformMouseClickOnView(allowlist_password_reuse_button);
525   EXPECT_THAT(
526       histograms.GetAllSamples(
527           safe_browsing::kEnterprisePasswordPageInfoHistogram),
528       testing::ElementsAre(
529           base::Bucket(static_cast<int>(safe_browsing::WarningAction::SHOWN),
530                        1),
531           base::Bucket(
532               static_cast<int>(safe_browsing::WarningAction::CHANGE_PASSWORD),
533               1),
534           base::Bucket(static_cast<int>(
535                            safe_browsing::WarningAction::MARK_AS_LEGITIMATE),
536                        1)));
537   // Security state will change after allowlisting.
538   visible_security_state = helper->GetVisibleSecurityState();
539   EXPECT_EQ(security_state::MALICIOUS_CONTENT_STATUS_NONE,
540             visible_security_state->malicious_content_status);
541 }
542 
543 // Test opening page info bubble that matches
544 // SB_THREAT_TYPE_SAVED_PASSWORD_REUSE threat type.
IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest,VerifySavedPasswordReusePageInfoBubble)545 IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest,
546                        VerifySavedPasswordReusePageInfoBubble) {
547   ASSERT_TRUE(embedded_test_server()->Start());
548   base::HistogramTester histograms;
549   ui_test_utils::NavigateToURL(browser(), embedded_test_server()->GetURL("/"));
550 
551   // Update security state of the current page to match
552   // SB_THREAT_TYPE_SAVED_PASSWORD_REUSE.
553   safe_browsing::ChromePasswordProtectionService* service =
554       safe_browsing::ChromePasswordProtectionService::
555           GetPasswordProtectionService(browser()->profile());
556   content::WebContents* contents =
557       browser()->tab_strip_model()->GetActiveWebContents();
558   safe_browsing::ReusedPasswordAccountType reused_password_account_type;
559   reused_password_account_type.set_account_type(
560       safe_browsing::ReusedPasswordAccountType::SAVED_PASSWORD);
561   service->set_reused_password_account_type_for_last_shown_warning(
562       reused_password_account_type);
563 
564   service->ShowModalWarning(
565       contents, safe_browsing::RequestOutcome::UNKNOWN,
566       safe_browsing::LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
567       "unused_token", reused_password_account_type);
568 
569   OpenPageInfoBubble(browser());
570   views::View* change_password_button = GetView(
571       browser(), PageInfoBubbleView::VIEW_ID_PAGE_INFO_BUTTON_CHANGE_PASSWORD);
572   views::View* allowlist_password_reuse_button = GetView(
573       browser(),
574       PageInfoBubbleView::VIEW_ID_PAGE_INFO_BUTTON_ALLOWLIST_PASSWORD_REUSE);
575 
576   SecurityStateTabHelper* helper =
577       SecurityStateTabHelper::FromWebContents(contents);
578   std::unique_ptr<security_state::VisibleSecurityState> visible_security_state =
579       helper->GetVisibleSecurityState();
580   ASSERT_EQ(security_state::MALICIOUS_CONTENT_STATUS_SAVED_PASSWORD_REUSE,
581             visible_security_state->malicious_content_status);
582 
583   // Verify these two buttons are showing.
584   EXPECT_TRUE(change_password_button->GetVisible());
585   EXPECT_TRUE(allowlist_password_reuse_button->GetVisible());
586 
587   // Verify clicking on button will increment corresponding bucket of
588   // PasswordProtection.PageInfoAction.NonGaiaEnterprisePasswordEntry histogram.
589   PerformMouseClickOnView(change_password_button);
590   EXPECT_THAT(
591       histograms.GetAllSamples(safe_browsing::kSavedPasswordPageInfoHistogram),
592       testing::ElementsAre(
593           base::Bucket(static_cast<int>(safe_browsing::WarningAction::SHOWN),
594                        1),
595           base::Bucket(
596               static_cast<int>(safe_browsing::WarningAction::CHANGE_PASSWORD),
597               1)));
598 
599   PerformMouseClickOnView(allowlist_password_reuse_button);
600   EXPECT_THAT(
601       histograms.GetAllSamples(safe_browsing::kSavedPasswordPageInfoHistogram),
602       testing::ElementsAre(
603           base::Bucket(static_cast<int>(safe_browsing::WarningAction::SHOWN),
604                        1),
605           base::Bucket(
606               static_cast<int>(safe_browsing::WarningAction::CHANGE_PASSWORD),
607               1),
608           base::Bucket(static_cast<int>(
609                            safe_browsing::WarningAction::MARK_AS_LEGITIMATE),
610                        1)));
611   // Security state will change after allowlisting.
612   visible_security_state = helper->GetVisibleSecurityState();
613   EXPECT_EQ(security_state::MALICIOUS_CONTENT_STATUS_NONE,
614             visible_security_state->malicious_content_status);
615 }
616 
617 // Shows the Page Info bubble for a HTTP page (specifically, about:blank).
IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest,InvokeUi_Insecure)618 IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest, InvokeUi_Insecure) {
619   ShowAndVerifyUi();
620 }
621 
622 // Shows the Page Info bubble for a HTTPS page.
IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest,InvokeUi_Secure)623 IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest, InvokeUi_Secure) {
624   ShowAndVerifyUi();
625 }
626 
IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest,InvokeUi_EvSecure)627 IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest, InvokeUi_EvSecure) {
628   ShowAndVerifyUi();
629 }
630 
631 // Shows the Page Info bubble for an internal page, e.g. chrome://settings.
IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest,InvokeUi_Internal)632 IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest, InvokeUi_Internal) {
633   ShowAndVerifyUi();
634 }
635 
636 // Shows the Page Info bubble for an extensions page.
IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest,InvokeUi_InternalExtension)637 IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest,
638                        InvokeUi_InternalExtension) {
639   ShowAndVerifyUi();
640 }
641 
642 // Shows the Page Info bubble for a chrome page that displays the source HTML.
IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest,InvokeUi_InternalViewSource)643 IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest,
644                        InvokeUi_InternalViewSource) {
645   ShowAndVerifyUi();
646 }
647 
648 // Shows the Page Info bubble for a file:// URL.
IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest,InvokeUi_File)649 IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest, InvokeUi_File) {
650   ShowAndVerifyUi();
651 }
652 
653 // Shows the Page Info bubble for a site flagged for malware by Safe Browsing.
IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest,InvokeUi_Malware)654 IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest, InvokeUi_Malware) {
655   ShowAndVerifyUi();
656 }
657 
658 // Shows the Page Info bubble for a site flagged for social engineering by Safe
659 // Browsing.
IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest,InvokeUi_Deceptive)660 IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest, InvokeUi_Deceptive) {
661   ShowAndVerifyUi();
662 }
663 
664 // Shows the Page Info bubble for a site flagged for distributing unwanted
665 // software by Safe Browsing.
IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest,InvokeUi_UnwantedSoftware)666 IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest,
667                        InvokeUi_UnwantedSoftware) {
668   ShowAndVerifyUi();
669 }
670 
671 // Shows the Page Info bubble Safe Browsing warning after detecting the user has
672 // re-used an existing password on a site, e.g. due to phishing.
IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest,InvokeUi_PasswordReuse)673 IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest, InvokeUi_PasswordReuse) {
674   ShowAndVerifyUi();
675 }
676 
677 // Shows the Page Info bubble for a site flagged for malware that also has a bad
678 // certificate.
IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest,InvokeUi_MalwareAndBadCert)679 IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest,
680                        InvokeUi_MalwareAndBadCert) {
681   ShowAndVerifyUi();
682 }
683 
684 // Shows the Page Info bubble for an admin-provided cert when the page is
685 // secure, but has a form that submits to an insecure url.
IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest,InvokeUi_MixedContentForm)686 IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest,
687                        InvokeUi_MixedContentForm) {
688   ShowAndVerifyUi();
689 }
690 
691 // Shows the Page Info bubble for an admin-provided cert when the page is
692 // secure, but it uses insecure resources (e.g. images).
IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest,InvokeUi_MixedContent)693 IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest, InvokeUi_MixedContent) {
694   ShowAndVerifyUi();
695 }
696 
697 // Shows the Page Info bubble with all the permissions displayed with 'Allow'
698 // set. All permissions will show regardless of its factory default value.
IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest,InvokeUi_AllowAllPermissions)699 IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest,
700                        InvokeUi_AllowAllPermissions) {
701   ShowAndVerifyUi();
702 }
703 
704 // Shows the Page Info bubble with all the permissions displayed with 'Block'
705 // set. All permissions will show regardless of its factory default value.
IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest,InvokeUi_BlockAllPermissions)706 IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest,
707                        InvokeUi_BlockAllPermissions) {
708   ShowAndVerifyUi();
709 }
710 
IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest,ClosesOnUserNavigateToSamePage)711 IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest,
712                        ClosesOnUserNavigateToSamePage) {
713   ui_test_utils::NavigateToURL(browser(), GetSimplePageUrl());
714   EXPECT_EQ(PageInfoBubbleView::BUBBLE_NONE,
715             PageInfoBubbleView::GetShownBubbleType());
716   OpenPageInfoBubble(browser());
717   EXPECT_EQ(PageInfoBubbleView::BUBBLE_INTERNAL_PAGE,
718             PageInfoBubbleView::GetShownBubbleType());
719   ui_test_utils::NavigateToURL(browser(), GetSimplePageUrl());
720   EXPECT_EQ(PageInfoBubbleView::BUBBLE_NONE,
721             PageInfoBubbleView::GetShownBubbleType());
722 }
723 
IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest,ClosesOnUserNavigateToDifferentPage)724 IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest,
725                        ClosesOnUserNavigateToDifferentPage) {
726   ui_test_utils::NavigateToURL(browser(), GetSimplePageUrl());
727   EXPECT_EQ(PageInfoBubbleView::BUBBLE_NONE,
728             PageInfoBubbleView::GetShownBubbleType());
729   OpenPageInfoBubble(browser());
730   EXPECT_EQ(PageInfoBubbleView::BUBBLE_INTERNAL_PAGE,
731             PageInfoBubbleView::GetShownBubbleType());
732   ui_test_utils::NavigateToURL(browser(), GetIframePageUrl());
733   EXPECT_EQ(PageInfoBubbleView::BUBBLE_NONE,
734             PageInfoBubbleView::GetShownBubbleType());
735 }
736 
IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest,DoesntCloseOnSubframeNavigate)737 IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest,
738                        DoesntCloseOnSubframeNavigate) {
739   ui_test_utils::NavigateToURL(browser(), GetIframePageUrl());
740   EXPECT_EQ(PageInfoBubbleView::BUBBLE_NONE,
741             PageInfoBubbleView::GetShownBubbleType());
742   OpenPageInfoBubble(browser());
743   EXPECT_EQ(PageInfoBubbleView::BUBBLE_INTERNAL_PAGE,
744             PageInfoBubbleView::GetShownBubbleType());
745   content::NavigateIframeToURL(
746       browser()->tab_strip_model()->GetActiveWebContents(), "test",
747       GetSimplePageUrl());
748   // Expect that the bubble is still open even after a subframe navigation has
749   // happened.
750   EXPECT_EQ(PageInfoBubbleView::BUBBLE_INTERNAL_PAGE,
751             PageInfoBubbleView::GetShownBubbleType());
752 }
753 
754 class PageInfoBubbleViewBrowserTestWithAutoupgradesDisabled
755     : public PageInfoBubbleViewBrowserTest {
756  public:
SetUpCommandLine(base::CommandLine * command_line)757   void SetUpCommandLine(base::CommandLine* command_line) override {
758     PageInfoBubbleViewBrowserTest::SetUpCommandLine(command_line);
759     feature_list.InitAndDisableFeature(
760         blink::features::kMixedContentAutoupgrade);
761   }
762 
763  private:
764   base::test::ScopedFeatureList feature_list;
765 };
766 
767 // Ensure changes to security state are reflected in an open PageInfo bubble.
IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTestWithAutoupgradesDisabled,UpdatesOnSecurityStateChange)768 IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTestWithAutoupgradesDisabled,
769                        UpdatesOnSecurityStateChange) {
770   net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
771   https_server.AddDefaultHandlers(
772       base::FilePath(FILE_PATH_LITERAL("chrome/test/data")));
773   ASSERT_TRUE(https_server.Start());
774 
775   ui_test_utils::NavigateToURL(
776       browser(), https_server.GetURL("/delayed_mixed_content.html"));
777   OpenPageInfoBubble(browser());
778 
779   views::BubbleDialogDelegateView* page_info =
780       PageInfoBubbleView::GetPageInfoBubbleForTesting();
781 
782   EXPECT_EQ(page_info->GetWindowTitle(),
783             l10n_util::GetStringUTF16(IDS_PAGE_INFO_SECURE_SUMMARY));
784 
785   ExecuteJavaScriptForTests("load_mixed();");
786   EXPECT_EQ(page_info->GetWindowTitle(),
787             l10n_util::GetStringUTF16(IDS_PAGE_INFO_MIXED_CONTENT_SUMMARY));
788 }
789 
790 // Ensure a page can both have an invalid certificate *and* be blocked by Safe
791 // Browsing.  Regression test for bug 869925.
IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest,BlockedAndInvalidCert)792 IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest, BlockedAndInvalidCert) {
793   net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
794   https_server.AddDefaultHandlers(
795       base::FilePath(FILE_PATH_LITERAL("chrome/test/data")));
796   ASSERT_TRUE(https_server.Start());
797 
798   ui_test_utils::NavigateToURL(browser(), https_server.GetURL("/simple.html"));
799 
800   // Setup the bogus identity with an expired cert and SB flagging.
801   PageInfoUI::IdentityInfo identity;
802   identity.identity_status = PageInfo::SITE_IDENTITY_STATUS_ERROR;
803   identity.certificate = net::ImportCertFromFile(net::GetTestCertsDirectory(),
804                                                  kExpiredCertificateFile);
805   identity.safe_browsing_status = PageInfo::SAFE_BROWSING_STATUS_MALWARE;
806   OpenPageInfoBubble(browser());
807 
808   SetPageInfoBubbleIdentityInfo(identity);
809 
810   views::BubbleDialogDelegateView* page_info =
811       PageInfoBubbleView::GetPageInfoBubbleForTesting();
812 
813   // Verify bubble complains of malware...
814   EXPECT_EQ(page_info->GetWindowTitle(),
815             l10n_util::GetStringUTF16(IDS_PAGE_INFO_MALWARE_SUMMARY));
816 
817   // ...and has a "Certificate (Invalid)" button.
818   const base::string16 invalid_parens = l10n_util::GetStringUTF16(
819       IDS_PAGE_INFO_CERTIFICATE_INVALID_PARENTHESIZED);
820   EXPECT_EQ(GetCertificateButtonTitle(),
821             l10n_util::GetStringFUTF16(IDS_PAGE_INFO_CERTIFICATE_BUTTON_TEXT,
822                                        invalid_parens));
823 }
824 
825 // Ensure a page that has an EV certificate *and* is blocked by Safe Browsing
826 // shows the correct PageInfo UI. Regression test for crbug.com/1014240.
IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest,MalwareAndEvCert)827 IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest, MalwareAndEvCert) {
828   net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
829   https_server.AddDefaultHandlers(
830       base::FilePath(FILE_PATH_LITERAL("chrome/test/data")));
831   ASSERT_TRUE(https_server.Start());
832 
833   ui_test_utils::NavigateToURL(browser(), https_server.GetURL("/simple.html"));
834 
835   // Generate a valid mock EV HTTPS identity, with an EV certificate. Must
836   // match conditions in PageInfoBubbleView::SetIdentityInfo() for setting
837   // the certificate button subtitle.
838   PageInfoUI::IdentityInfo identity;
839   identity.identity_status = PageInfo::SITE_IDENTITY_STATUS_EV_CERT;
840   identity.connection_status = PageInfo::SITE_CONNECTION_STATUS_ENCRYPTED;
841   scoped_refptr<net::X509Certificate> ev_cert =
842       net::X509Certificate::CreateFromBytes(
843           reinterpret_cast<const char*>(thawte_der), sizeof(thawte_der));
844   ASSERT_TRUE(ev_cert);
845   identity.certificate = ev_cert;
846 
847   // Have the page also trigger an SB malware warning.
848   identity.safe_browsing_status = PageInfo::SAFE_BROWSING_STATUS_MALWARE;
849 
850   OpenPageInfoBubble(browser());
851   SetPageInfoBubbleIdentityInfo(identity);
852 
853   views::BubbleDialogDelegateView* page_info =
854       PageInfoBubbleView::GetPageInfoBubbleForTesting();
855 
856   // Verify bubble complains of malware...
857   EXPECT_EQ(page_info->GetWindowTitle(),
858             l10n_util::GetStringUTF16(IDS_PAGE_INFO_MALWARE_SUMMARY));
859 
860   // ...and has the correct organization details in the Certificate button.
861   EXPECT_EQ(GetCertificateButtonSubtitle(),
862             l10n_util::GetStringFUTF16(
863                 IDS_PAGE_INFO_SECURITY_TAB_SECURE_IDENTITY_EV_VERIFIED,
864                 base::UTF8ToUTF16("Thawte Inc"), base::UTF8ToUTF16("US")));
865 }
866 
867 namespace {
868 
869 // Tracks focus of an arbitrary UI element.
870 class FocusTracker {
871  public:
focused() const872   bool focused() const { return focused_; }
873 
874   // Wait for focused() to be in state |target_state_is_focused|. If focused()
875   // is already in the desired state, returns immediately, otherwise waits until
876   // it is.
WaitForFocus(bool target_state_is_focused)877   void WaitForFocus(bool target_state_is_focused) {
878     if (focused_ == target_state_is_focused)
879       return;
880     target_state_is_focused_ = target_state_is_focused;
881     run_loop_.Run();
882   }
883 
884  protected:
FocusTracker(bool initially_focused)885   explicit FocusTracker(bool initially_focused) : focused_(initially_focused) {}
~FocusTracker()886   virtual ~FocusTracker() {}
887 
OnFocused()888   void OnFocused() {
889     focused_ = true;
890     if (run_loop_.running() && target_state_is_focused_ == focused_)
891       run_loop_.Quit();
892   }
893 
OnBlurred()894   void OnBlurred() {
895     focused_ = false;
896     if (run_loop_.running() && target_state_is_focused_ == focused_)
897       run_loop_.Quit();
898   }
899 
900  private:
901   // Whether the tracked visual element is currently focused.
902   bool focused_ = false;
903 
904   // Desired state when waiting for focus to change.
905   bool target_state_is_focused_;
906 
907   base::RunLoop run_loop_;
908 
909   DISALLOW_COPY_AND_ASSIGN(FocusTracker);
910 };
911 
912 // Watches a WebContents for focus changes.
913 class WebContentsFocusTracker : public FocusTracker,
914                                 public content::WebContentsObserver {
915  public:
WebContentsFocusTracker(content::WebContents * web_contents)916   explicit WebContentsFocusTracker(content::WebContents* web_contents)
917       : FocusTracker(IsWebContentsFocused(web_contents)),
918         WebContentsObserver(web_contents) {}
919 
OnWebContentsFocused(content::RenderWidgetHost * render_widget_host)920   void OnWebContentsFocused(
921       content::RenderWidgetHost* render_widget_host) override {
922     OnFocused();
923   }
924 
OnWebContentsLostFocus(content::RenderWidgetHost * render_widget_host)925   void OnWebContentsLostFocus(
926       content::RenderWidgetHost* render_widget_host) override {
927     OnBlurred();
928   }
929 
930  private:
IsWebContentsFocused(content::WebContents * web_contents)931   static bool IsWebContentsFocused(content::WebContents* web_contents) {
932     Browser* const browser = chrome::FindBrowserWithWebContents(web_contents);
933     if (!browser)
934       return false;
935     if (browser->tab_strip_model()->GetActiveWebContents() != web_contents)
936       return false;
937     return BrowserView::GetBrowserViewForBrowser(browser)
938         ->contents_web_view()
939         ->HasFocus();
940   }
941 };
942 
943 // Watches a View for focus changes.
944 class ViewFocusTracker : public FocusTracker, public views::ViewObserver {
945  public:
ViewFocusTracker(views::View * view)946   explicit ViewFocusTracker(views::View* view)
947       : FocusTracker(view->HasFocus()) {
948     scoped_observer_.Add(view);
949   }
950 
OnViewFocused(views::View * observed_view)951   void OnViewFocused(views::View* observed_view) override { OnFocused(); }
952 
OnViewBlurred(views::View * observed_view)953   void OnViewBlurred(views::View* observed_view) override { OnBlurred(); }
954 
955  private:
956   ScopedObserver<views::View, views::ViewObserver> scoped_observer_{this};
957 };
958 
959 }  // namespace
960 
961 #if defined(OS_MAC)
962 // https://crbug.com/1029882
963 #define MAYBE_FocusReturnsToContentOnClose DISABLED_FocusReturnsToContentOnClose
964 #else
965 #define MAYBE_FocusReturnsToContentOnClose FocusReturnsToContentOnClose
966 #endif
967 
968 // Test that when the PageInfo bubble is closed, focus is returned to the web
969 // contents pane.
IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest,MAYBE_FocusReturnsToContentOnClose)970 IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest,
971                        MAYBE_FocusReturnsToContentOnClose) {
972   content::WebContents* const web_contents =
973       browser()->tab_strip_model()->GetActiveWebContents();
974   WebContentsFocusTracker web_contents_focus_tracker(web_contents);
975   web_contents->Focus();
976   web_contents_focus_tracker.WaitForFocus(true);
977 
978   OpenPageInfoBubble(browser());
979   PageInfoBubbleView* page_info_bubble_view = static_cast<PageInfoBubbleView*>(
980       PageInfoBubbleView::GetPageInfoBubbleForTesting());
981   EXPECT_FALSE(web_contents_focus_tracker.focused());
982 
983   page_info_bubble_view->GetWidget()->CloseWithReason(
984       views::Widget::ClosedReason::kEscKeyPressed);
985   web_contents_focus_tracker.WaitForFocus(true);
986   EXPECT_TRUE(web_contents_focus_tracker.focused());
987 }
988 
989 #if defined(OS_MAC)
990 // https://crbug.com/1029882
991 #define MAYBE_FocusDoesNotReturnToContentsOnReloadPrompt \
992   DISABLED_FocusDoesNotReturnToContentsOnReloadPrompt
993 #else
994 #define MAYBE_FocusDoesNotReturnToContentsOnReloadPrompt \
995   FocusDoesNotReturnToContentsOnReloadPrompt
996 #endif
997 
998 // Test that when the PageInfo bubble is closed and a reload prompt is
999 // displayed, focus is NOT returned to the web contents pane, but rather returns
1000 // to the location bar so accessibility users must tab through the reload prompt
1001 // before getting back to web contents (see https://crbug.com/910067).
IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest,MAYBE_FocusDoesNotReturnToContentsOnReloadPrompt)1002 IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest,
1003                        MAYBE_FocusDoesNotReturnToContentsOnReloadPrompt) {
1004   content::WebContents* const web_contents =
1005       browser()->tab_strip_model()->GetActiveWebContents();
1006   WebContentsFocusTracker web_contents_focus_tracker(web_contents);
1007   ViewFocusTracker location_bar_focus_tracker(
1008       BrowserView::GetBrowserViewForBrowser(browser())->GetLocationBarView());
1009   web_contents->Focus();
1010   web_contents_focus_tracker.WaitForFocus(true);
1011 
1012   OpenPageInfoBubble(browser());
1013   PageInfoBubbleView* page_info_bubble_view = static_cast<PageInfoBubbleView*>(
1014       PageInfoBubbleView::GetPageInfoBubbleForTesting());
1015   EXPECT_FALSE(web_contents_focus_tracker.focused());
1016 
1017   TriggerReloadPromptOnClose();
1018   page_info_bubble_view->GetWidget()->CloseWithReason(
1019       views::Widget::ClosedReason::kEscKeyPressed);
1020   location_bar_focus_tracker.WaitForFocus(true);
1021   web_contents_focus_tracker.WaitForFocus(false);
1022   EXPECT_TRUE(location_bar_focus_tracker.focused());
1023   EXPECT_FALSE(web_contents_focus_tracker.focused());
1024 }
1025