blob: 4c6c913a3dc84390f9aa34f6917a7cc2ccb7c259 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/captive_portal/captive_portal_tab_helper.h"
#include "base/bind.h"
#include "base/debug/dump_without_crashing.h"
#include "chrome/browser/captive_portal/captive_portal_login_detector.h"
#include "chrome/browser/captive_portal/captive_portal_service_factory.h"
#include "chrome/browser/captive_portal/captive_portal_tab_reloader.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/browser_tabstrip.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/notification_details.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_source.h"
#include "content/public/browser/notification_types.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/web_contents.h"
#include "net/base/net_errors.h"
#include "net/ssl/ssl_info.h"
using captive_portal::CaptivePortalResult;
DEFINE_WEB_CONTENTS_USER_DATA_KEY(CaptivePortalTabHelper);
CaptivePortalTabHelper::CaptivePortalTabHelper(
content::WebContents* web_contents)
: content::WebContentsObserver(web_contents),
profile_(Profile::FromBrowserContext(web_contents->GetBrowserContext())),
navigation_handle_(nullptr),
tab_reloader_(new CaptivePortalTabReloader(
profile_,
web_contents,
base::Bind(&CaptivePortalTabHelper::OpenLoginTabForWebContents,
web_contents,
false))),
login_detector_(new CaptivePortalLoginDetector(profile_)) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
registrar_.Add(this,
chrome::NOTIFICATION_CAPTIVE_PORTAL_CHECK_RESULT,
content::Source<Profile>(profile_));
}
CaptivePortalTabHelper::~CaptivePortalTabHelper() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
}
void CaptivePortalTabHelper::DidStartNavigation(
content::NavigationHandle* navigation_handle) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (!navigation_handle->IsInMainFrame() ||
navigation_handle->IsSameDocument()) {
return;
}
// TODO(clamy): The root cause behind crbug.com/704892 is known.
// Remove this code if it is never reached until ~ 2017-July-20.
if (navigation_handle == navigation_handle_)
base::debug::DumpWithoutCrashing();
bool was_tracking_navigation = !!navigation_handle_;
navigation_handle_ = navigation_handle;
// Always track the latest navigation. If a navigation was already tracked,
// and it committed (either the navigation proper or an error page), it is
// safe to start tracking the new navigation. Otherwise simulate an abort
// before reporting the start of the new navigation.
if (was_tracking_navigation)
tab_reloader_->OnAbort();
tab_reloader_->OnLoadStart(
navigation_handle->GetURL().SchemeIsCryptographic());
}
void CaptivePortalTabHelper::DidRedirectNavigation(
content::NavigationHandle* navigation_handle) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (navigation_handle != navigation_handle_)
return;
DCHECK(navigation_handle->IsInMainFrame());
tab_reloader_->OnRedirect(
navigation_handle->GetURL().SchemeIsCryptographic());
}
void CaptivePortalTabHelper::DidFinishNavigation(
content::NavigationHandle* navigation_handle) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
// Exclude subframe navigations.
if (!navigation_handle->IsInMainFrame())
return;
// Exclude same-document navigations and aborted navigations that were not
// being tracked.
if (navigation_handle_ != navigation_handle &&
(!navigation_handle->HasCommitted() ||
navigation_handle->IsSameDocument())) {
return;
}
bool need_to_simulate_start = navigation_handle_ != navigation_handle;
bool need_to_simulate_previous_abort =
need_to_simulate_start && !!navigation_handle_;
navigation_handle_ = nullptr;
if (need_to_simulate_previous_abort)
tab_reloader_->OnAbort();
if (need_to_simulate_start) {
tab_reloader_->OnLoadStart(
navigation_handle->GetURL().SchemeIsCryptographic());
}
if (navigation_handle->HasCommitted()) {
tab_reloader_->OnLoadCommitted(navigation_handle->GetNetErrorCode());
} else {
tab_reloader_->OnAbort();
}
}
void CaptivePortalTabHelper::DidStopLoading() {
login_detector_->OnStoppedLoading();
}
void CaptivePortalTabHelper::Observe(
int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
DCHECK_EQ(chrome::NOTIFICATION_CAPTIVE_PORTAL_CHECK_RESULT, type);
DCHECK_EQ(profile_, content::Source<Profile>(source).ptr());
const CaptivePortalService::Results* results =
content::Details<CaptivePortalService::Results>(details).ptr();
OnCaptivePortalResults(results->previous_result, results->result);
}
void CaptivePortalTabHelper::OnSSLCertError(const net::SSLInfo& ssl_info) {
tab_reloader_->OnSSLCertError(ssl_info);
}
bool CaptivePortalTabHelper::IsLoginTab() const {
return login_detector_->is_login_tab();
}
// static
void CaptivePortalTabHelper::OpenLoginTabForWebContents(
content::WebContents* web_contents,
bool focus) {
Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
// If the Profile doesn't have a tabbed browser window open, do nothing.
if (!browser)
return;
// Check if the Profile's topmost browser window already has a login tab.
// If so, do nothing.
// TODO(mmenke): Consider focusing that tab, at least if this is the tab
// helper for the currently active tab for the profile.
for (int i = 0; i < browser->tab_strip_model()->count(); ++i) {
content::WebContents* contents =
browser->tab_strip_model()->GetWebContentsAt(i);
CaptivePortalTabHelper* captive_portal_tab_helper =
CaptivePortalTabHelper::FromWebContents(contents);
if (captive_portal_tab_helper->IsLoginTab()) {
if (focus)
browser->tab_strip_model()->ActivateTabAt(i, false);
return;
}
}
// Otherwise, open a login tab. Only end up here when a captive portal result
// was received, so it's safe to assume profile has a CaptivePortalService.
content::WebContents* new_contents = chrome::AddSelectedTabWithURL(
browser,
CaptivePortalServiceFactory::GetForProfile(
browser->profile())->test_url(),
ui::PAGE_TRANSITION_TYPED);
CaptivePortalTabHelper* captive_portal_tab_helper =
CaptivePortalTabHelper::FromWebContents(new_contents);
captive_portal_tab_helper->SetIsLoginTab();
}
void CaptivePortalTabHelper::OnCaptivePortalResults(
CaptivePortalResult previous_result,
CaptivePortalResult result) {
tab_reloader_->OnCaptivePortalResults(previous_result, result);
login_detector_->OnCaptivePortalResults(previous_result, result);
}
void CaptivePortalTabHelper::SetIsLoginTab() {
login_detector_->SetIsLoginTab();
}
void CaptivePortalTabHelper::SetTabReloaderForTest(
CaptivePortalTabReloader* tab_reloader) {
tab_reloader_.reset(tab_reloader);
}
CaptivePortalTabReloader* CaptivePortalTabHelper::GetTabReloaderForTest() {
return tab_reloader_.get();
}