[Metrics] Make MetricsStateManager take a callback param to check if UMA is enabled.
[chromium-blink-merge.git] / chrome / browser / captive_portal / captive_portal_tab_helper.cc
blobd939bdaec5a99cb906a6acea4c9fc02fa199e557
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.
5 #include "chrome/browser/captive_portal/captive_portal_tab_helper.h"
7 #include "base/bind.h"
8 #include "chrome/browser/captive_portal/captive_portal_login_detector.h"
9 #include "chrome/browser/captive_portal/captive_portal_service_factory.h"
10 #include "chrome/browser/captive_portal/captive_portal_tab_reloader.h"
11 #include "chrome/browser/chrome_notification_types.h"
12 #include "chrome/browser/profiles/profile.h"
13 #include "chrome/browser/ui/browser.h"
14 #include "chrome/browser/ui/browser_finder.h"
15 #include "chrome/browser/ui/browser_tabstrip.h"
16 #include "chrome/browser/ui/tabs/tab_strip_model.h"
17 #include "content/public/browser/notification_details.h"
18 #include "content/public/browser/notification_service.h"
19 #include "content/public/browser/notification_source.h"
20 #include "content/public/browser/notification_types.h"
21 #include "content/public/browser/render_process_host.h"
22 #include "content/public/browser/render_view_host.h"
23 #include "content/public/browser/resource_request_details.h"
24 #include "content/public/browser/web_contents.h"
25 #include "net/base/net_errors.h"
26 #include "net/ssl/ssl_info.h"
28 using captive_portal::CaptivePortalResult;
30 DEFINE_WEB_CONTENTS_USER_DATA_KEY(CaptivePortalTabHelper);
32 CaptivePortalTabHelper::CaptivePortalTabHelper(
33 content::WebContents* web_contents)
34 : content::WebContentsObserver(web_contents),
35 // web_contents is NULL in unit tests.
36 profile_(web_contents ? Profile::FromBrowserContext(
37 web_contents->GetBrowserContext())
38 : NULL),
39 tab_reloader_(
40 new CaptivePortalTabReloader(
41 profile_,
42 web_contents,
43 base::Bind(&CaptivePortalTabHelper::OpenLoginTab,
44 base::Unretained(this)))),
45 login_detector_(new CaptivePortalLoginDetector(profile_)),
46 web_contents_(web_contents),
47 pending_error_code_(net::OK),
48 provisional_render_view_host_(NULL) {
49 registrar_.Add(this,
50 chrome::NOTIFICATION_CAPTIVE_PORTAL_CHECK_RESULT,
51 content::Source<Profile>(profile_));
52 registrar_.Add(this,
53 content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT,
54 content::Source<content::WebContents>(web_contents));
57 CaptivePortalTabHelper::~CaptivePortalTabHelper() {
60 void CaptivePortalTabHelper::RenderViewDeleted(
61 content::RenderViewHost* render_view_host) {
62 // This can happen when a cross-process navigation is aborted, either by
63 // pressing stop or by starting a new cross-process navigation that can't
64 // re-use |provisional_render_view_host_|. May also happen on a crash.
65 if (render_view_host == provisional_render_view_host_)
66 OnLoadAborted();
69 void CaptivePortalTabHelper::DidStartProvisionalLoadForFrame(
70 int64 frame_id,
71 int64 parent_frame_id,
72 bool is_main_frame,
73 const GURL& validated_url,
74 bool is_error_page,
75 bool is_iframe_srcdoc,
76 content::RenderViewHost* render_view_host) {
77 DCHECK(CalledOnValidThread());
79 // Ignore subframes.
80 if (!is_main_frame)
81 return;
83 if (provisional_render_view_host_) {
84 // If loading an error page for a previous failure, treat this as part of
85 // the previous load. Link Doctor pages act like two error page loads in a
86 // row. The second time, provisional_render_view_host_ will be NULL.
87 if (is_error_page && provisional_render_view_host_ == render_view_host)
88 return;
89 // Otherwise, abort the old load.
90 OnLoadAborted();
93 provisional_render_view_host_ = render_view_host;
94 pending_error_code_ = net::OK;
96 tab_reloader_->OnLoadStart(validated_url.SchemeIsSecure());
99 void CaptivePortalTabHelper::DidCommitProvisionalLoadForFrame(
100 int64 frame_id,
101 const base::string16& frame_unique_name,
102 bool is_main_frame,
103 const GURL& url,
104 content::PageTransition transition_type,
105 content::RenderViewHost* render_view_host) {
106 DCHECK(CalledOnValidThread());
108 // Ignore subframes.
109 if (!is_main_frame)
110 return;
112 if (provisional_render_view_host_ == render_view_host) {
113 tab_reloader_->OnLoadCommitted(pending_error_code_);
114 } else {
115 // This may happen if the active RenderView commits a page before a cross
116 // process navigation cancels the old load. In this case, the commit of the
117 // old navigation will cancel the newer one.
118 OnLoadAborted();
120 // Send information about the new load.
121 tab_reloader_->OnLoadStart(url.SchemeIsSecure());
122 tab_reloader_->OnLoadCommitted(net::OK);
125 provisional_render_view_host_ = NULL;
126 pending_error_code_ = net::OK;
129 void CaptivePortalTabHelper::DidFailProvisionalLoad(
130 int64 frame_id,
131 const base::string16& frame_unique_name,
132 bool is_main_frame,
133 const GURL& validated_url,
134 int error_code,
135 const base::string16& error_description,
136 content::RenderViewHost* render_view_host) {
137 DCHECK(CalledOnValidThread());
139 // Ignore subframes and unexpected RenderViewHosts.
140 if (!is_main_frame || render_view_host != provisional_render_view_host_)
141 return;
143 // Aborts generally aren't followed by loading an error page, so go ahead and
144 // reset the state now, to prevent any captive portal checks from triggering.
145 if (error_code == net::ERR_ABORTED) {
146 OnLoadAborted();
147 return;
150 pending_error_code_ = error_code;
153 void CaptivePortalTabHelper::DidStopLoading(
154 content::RenderViewHost* render_view_host) {
155 DCHECK(CalledOnValidThread());
157 login_detector_->OnStoppedLoading();
160 void CaptivePortalTabHelper::Observe(
161 int type,
162 const content::NotificationSource& source,
163 const content::NotificationDetails& details) {
164 DCHECK(CalledOnValidThread());
165 switch (type) {
166 case content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT: {
167 DCHECK_EQ(web_contents(),
168 content::Source<content::WebContents>(source).ptr());
170 const content::ResourceRedirectDetails* redirect_details =
171 content::Details<content::ResourceRedirectDetails>(details).ptr();
173 OnRedirect(redirect_details->origin_child_id,
174 redirect_details->resource_type,
175 redirect_details->new_url);
176 break;
178 case chrome::NOTIFICATION_CAPTIVE_PORTAL_CHECK_RESULT: {
179 DCHECK_EQ(profile_, content::Source<Profile>(source).ptr());
181 const CaptivePortalService::Results* results =
182 content::Details<CaptivePortalService::Results>(details).ptr();
184 OnCaptivePortalResults(results->previous_result, results->result);
185 break;
187 default:
188 NOTREACHED();
192 void CaptivePortalTabHelper::OnSSLCertError(const net::SSLInfo& ssl_info) {
193 tab_reloader_->OnSSLCertError(ssl_info);
196 bool CaptivePortalTabHelper::IsLoginTab() const {
197 return login_detector_->is_login_tab();
200 void CaptivePortalTabHelper::OnRedirect(int child_id,
201 ResourceType::Type resource_type,
202 const GURL& new_url) {
203 // Only main frame redirects for the provisional RenderViewHost matter.
204 if (resource_type != ResourceType::MAIN_FRAME ||
205 !provisional_render_view_host_ ||
206 provisional_render_view_host_->GetProcess()->GetID() != child_id) {
207 return;
210 tab_reloader_->OnRedirect(new_url.SchemeIsSecure());
213 void CaptivePortalTabHelper::OnCaptivePortalResults(
214 CaptivePortalResult previous_result,
215 CaptivePortalResult result) {
216 tab_reloader_->OnCaptivePortalResults(previous_result, result);
217 login_detector_->OnCaptivePortalResults(previous_result, result);
220 void CaptivePortalTabHelper::OnLoadAborted() {
221 // No further messages for the cancelled navigation will occur.
222 provisional_render_view_host_ = NULL;
223 // May have been aborting the load of an error page.
224 pending_error_code_ = net::OK;
226 tab_reloader_->OnAbort();
229 void CaptivePortalTabHelper::SetIsLoginTab() {
230 login_detector_->SetIsLoginTab();
233 void CaptivePortalTabHelper::SetTabReloaderForTest(
234 CaptivePortalTabReloader* tab_reloader) {
235 tab_reloader_.reset(tab_reloader);
238 CaptivePortalTabReloader* CaptivePortalTabHelper::GetTabReloaderForTest() {
239 return tab_reloader_.get();
242 void CaptivePortalTabHelper::OpenLoginTab() {
243 Browser* browser = chrome::FindBrowserWithWebContents(web_contents_);
245 // If the Profile doesn't have a tabbed browser window open, do nothing.
246 if (!browser)
247 return;
249 // Check if the Profile's topmost browser window already has a login tab.
250 // If so, do nothing.
251 // TODO(mmenke): Consider focusing that tab, at least if this is the tab
252 // helper for the currently active tab for the profile.
253 for (int i = 0; i < browser->tab_strip_model()->count(); ++i) {
254 content::WebContents* web_contents =
255 browser->tab_strip_model()->GetWebContentsAt(i);
256 CaptivePortalTabHelper* captive_portal_tab_helper =
257 CaptivePortalTabHelper::FromWebContents(web_contents);
258 if (captive_portal_tab_helper->IsLoginTab())
259 return;
262 // Otherwise, open a login tab. Only end up here when a captive portal result
263 // was received, so it's safe to assume |profile_| has a CaptivePortalService.
264 content::WebContents* web_contents = chrome::AddSelectedTabWithURL(
265 browser,
266 CaptivePortalServiceFactory::GetForProfile(profile_)->test_url(),
267 content::PAGE_TRANSITION_TYPED);
268 CaptivePortalTabHelper* captive_portal_tab_helper =
269 CaptivePortalTabHelper::FromWebContents(web_contents);
270 captive_portal_tab_helper->SetIsLoginTab();