- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / captive_portal / captive_portal_tab_helper.cc
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 #include "chrome/browser/captive_portal/captive_portal_tab_helper.h"
6
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"
27
28 DEFINE_WEB_CONTENTS_USER_DATA_KEY(captive_portal::CaptivePortalTabHelper);
29
30 namespace captive_portal {
31
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));
55 }
56
57 CaptivePortalTabHelper::~CaptivePortalTabHelper() {
58 }
59
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();
67 }
68
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());
78
79   // Ignore subframes.
80   if (!is_main_frame)
81     return;
82
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();
91   }
92
93   provisional_render_view_host_ = render_view_host;
94   pending_error_code_ = net::OK;
95
96   tab_reloader_->OnLoadStart(validated_url.SchemeIsSecure());
97 }
98
99 void CaptivePortalTabHelper::DidCommitProvisionalLoadForFrame(
100     int64 frame_id,
101     const 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());
107
108   // Ignore subframes.
109   if (!is_main_frame)
110     return;
111
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();
119
120     // Send information about the new load.
121     tab_reloader_->OnLoadStart(url.SchemeIsSecure());
122     tab_reloader_->OnLoadCommitted(net::OK);
123   }
124
125   provisional_render_view_host_ = NULL;
126   pending_error_code_ = net::OK;
127 }
128
129 void CaptivePortalTabHelper::DidFailProvisionalLoad(
130     int64 frame_id,
131     const string16& frame_unique_name,
132     bool is_main_frame,
133     const GURL& validated_url,
134     int error_code,
135     const string16& error_description,
136     content::RenderViewHost* render_view_host) {
137   DCHECK(CalledOnValidThread());
138
139   // Ignore subframes and unexpected RenderViewHosts.
140   if (!is_main_frame || render_view_host != provisional_render_view_host_)
141     return;
142
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;
148   }
149
150   pending_error_code_ = error_code;
151 }
152
153 void CaptivePortalTabHelper::DidStopLoading(
154     content::RenderViewHost* render_view_host) {
155   DCHECK(CalledOnValidThread());
156
157   login_detector_->OnStoppedLoading();
158 }
159
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());
169
170       const content::ResourceRedirectDetails* redirect_details =
171           content::Details<content::ResourceRedirectDetails>(details).ptr();
172
173       OnRedirect(redirect_details->origin_child_id,
174                  redirect_details->resource_type,
175                  redirect_details->new_url);
176       break;
177     }
178     case chrome::NOTIFICATION_CAPTIVE_PORTAL_CHECK_RESULT: {
179       DCHECK_EQ(profile_, content::Source<Profile>(source).ptr());
180
181       const CaptivePortalService::Results* results =
182           content::Details<CaptivePortalService::Results>(details).ptr();
183
184       OnCaptivePortalResults(results->previous_result, results->result);
185       break;
186     }
187     default:
188       NOTREACHED();
189   }
190 }
191
192 void CaptivePortalTabHelper::OnSSLCertError(const net::SSLInfo& ssl_info) {
193   tab_reloader_->OnSSLCertError(ssl_info);
194 }
195
196 bool CaptivePortalTabHelper::IsLoginTab() const {
197   return login_detector_->is_login_tab();
198 }
199
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;
208   }
209
210   tab_reloader_->OnRedirect(new_url.SchemeIsSecure());
211 }
212
213 void CaptivePortalTabHelper::OnCaptivePortalResults(Result previous_result,
214                                                     Result result) {
215   tab_reloader_->OnCaptivePortalResults(previous_result, result);
216   login_detector_->OnCaptivePortalResults(previous_result, result);
217 }
218
219 void CaptivePortalTabHelper::OnLoadAborted() {
220   // No further messages for the cancelled navigation will occur.
221   provisional_render_view_host_ = NULL;
222   // May have been aborting the load of an error page.
223   pending_error_code_ = net::OK;
224
225   tab_reloader_->OnAbort();
226 }
227
228 void CaptivePortalTabHelper::SetIsLoginTab() {
229   login_detector_->SetIsLoginTab();
230 }
231
232 void CaptivePortalTabHelper::SetTabReloaderForTest(
233     CaptivePortalTabReloader* tab_reloader) {
234   tab_reloader_.reset(tab_reloader);
235 }
236
237 CaptivePortalTabReloader* CaptivePortalTabHelper::GetTabReloaderForTest() {
238   return tab_reloader_.get();
239 }
240
241 void CaptivePortalTabHelper::OpenLoginTab() {
242   Browser* browser = chrome::FindBrowserWithWebContents(web_contents_);
243
244   // If the Profile doesn't have a tabbed browser window open, do nothing.
245   if (!browser)
246     return;
247
248   // Check if the Profile's topmost browser window already has a login tab.
249   // If so, do nothing.
250   // TODO(mmenke):  Consider focusing that tab, at least if this is the tab
251   //                helper for the currently active tab for the profile.
252   for (int i = 0; i < browser->tab_strip_model()->count(); ++i) {
253     content::WebContents* web_contents =
254         browser->tab_strip_model()->GetWebContentsAt(i);
255     captive_portal::CaptivePortalTabHelper* captive_portal_tab_helper =
256         captive_portal::CaptivePortalTabHelper::FromWebContents(web_contents);
257     if (captive_portal_tab_helper->IsLoginTab())
258       return;
259   }
260
261   // Otherwise, open a login tab.  Only end up here when a captive portal result
262   // was received, so it's safe to assume |profile_| has a CaptivePortalService.
263   content::WebContents* web_contents = chrome::AddSelectedTabWithURL(
264           browser,
265           CaptivePortalServiceFactory::GetForProfile(profile_)->test_url(),
266           content::PAGE_TRANSITION_TYPED);
267   captive_portal::CaptivePortalTabHelper* captive_portal_tab_helper =
268       captive_portal::CaptivePortalTabHelper::FromWebContents(web_contents);
269   captive_portal_tab_helper->SetIsLoginTab();
270 }
271
272 }  // namespace captive_portal