| Index: chrome/browser/captive_portal/captive_portal_tab_helper.cc
|
| ===================================================================
|
| --- chrome/browser/captive_portal/captive_portal_tab_helper.cc (revision 0)
|
| +++ chrome/browser/captive_portal/captive_portal_tab_helper.cc (revision 0)
|
| @@ -0,0 +1,308 @@
|
| +// 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/message_loop.h"
|
| +#include "chrome/browser/captive_portal/captive_portal_service.h"
|
| +#include "chrome/browser/captive_portal/captive_portal_service_factory.h"
|
| +#include "chrome/browser/profiles/profile.h"
|
| +#include "chrome/browser/ui/browser.h"
|
| +#include "chrome/browser/ui/browser_finder.h"
|
| +#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
|
| +#include "chrome/common/chrome_notification_types.h"
|
| +#include "content/public/browser/navigation_controller.h"
|
| +#include "content/public/browser/navigation_entry.h"
|
| +#include "content/public/browser/notification_details.h"
|
| +#include "content/public/browser/notification_source.h"
|
| +#include "content/public/browser/web_contents.h"
|
| +#include "net/base/net_errors.h"
|
| +
|
| +namespace captive_portal {
|
| +
|
| +namespace {
|
| +
|
| +// The time to wait for a slow loading SSL page before triggering a captive
|
| +// portal check.
|
| +const int kDefaultSlowSSLTimeSeconds = 30;
|
| +
|
| +} // namespace
|
| +
|
| +CaptivePortalTabHelper::CaptivePortalTabHelper(
|
| + Profile* profile,
|
| + content::WebContents* web_contents)
|
| + : ALLOW_THIS_IN_INITIALIZER_LIST(observer_(this, web_contents)),
|
| + profile_(profile),
|
| + state_(STATE_NONE),
|
| + provisional_main_frame_load_(false),
|
| + slow_ssl_load_time_(
|
| + base::TimeDelta::FromSeconds(kDefaultSlowSSLTimeSeconds)),
|
| + ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {
|
| + registrar_.Add(this,
|
| + chrome::NOTIFICATION_CAPTIVE_PORTAL_CHECK_RESULT,
|
| + content::Source<Profile>(profile_));
|
| +}
|
| +
|
| +CaptivePortalTabHelper::~CaptivePortalTabHelper() {
|
| +}
|
| +
|
| +void CaptivePortalTabHelper::OnLoadStart(bool is_ssl) {
|
| + DCHECK(CalledOnValidThread());
|
| +
|
| + provisional_main_frame_load_ = true;
|
| +
|
| + // If this is a login tab, do nothing.
|
| + if (state_ == STATE_CAPTIVE_PORTAL_LOGIN_PAGE)
|
| + return;
|
| +
|
| + // Otherwise, reset the state.
|
| + SetState(STATE_NONE);
|
| +
|
| + // Start the slow load timer for SSL pages.
|
| + // TODO(mmenke): Should this look at the port instead? The reason the
|
| + // request never connects is because of the port, not the
|
| + // protocol.
|
| + if (is_ssl)
|
| + SetState(STATE_TIMER_RUNNING);
|
| +}
|
| +
|
| +void CaptivePortalTabHelper::OnLoadCommitted(int net_error) {
|
| + DCHECK(CalledOnValidThread());
|
| +
|
| + provisional_main_frame_load_ = false;
|
| +
|
| + if (state_ == STATE_CAPTIVE_PORTAL_LOGIN_PAGE || state_ == STATE_NONE)
|
| + return;
|
| +
|
| + // If there's no timeout error, reset the state.
|
| + if (net_error != net::ERR_CONNECTION_TIMED_OUT) {
|
| + // TODO(mmenke): If the new URL is the same as the old broken URL, and the
|
| + // request succeeds, should probably trigger another
|
| + // captive portal check.
|
| + SetState(STATE_NONE);
|
| + return;
|
| + }
|
| +
|
| + // The page timed out before the timer triggered. This is not terribly
|
| + // likely, but if it does happen, the tab may have been broken by a captive
|
| + // portal. Go ahead and try to detect a portal now, rather than waiting for
|
| + // the timer.
|
| + if (state_ == STATE_TIMER_RUNNING) {
|
| + OnSlowSSLConnect();
|
| + return;
|
| + }
|
| +
|
| + // If the tab needs to reload, do so asynchronously, to avoid reentrancy
|
| + // issues.
|
| + if (state_ == STATE_NEEDS_RELOAD) {
|
| + MessageLoop::current()->PostTask(
|
| + FROM_HERE,
|
| + base::Bind(&CaptivePortalTabHelper::ReloadTabIfNeeded,
|
| + weak_factory_.GetWeakPtr()));
|
| + }
|
| +}
|
| +
|
| +void CaptivePortalTabHelper::OnAbort() {
|
| + DCHECK(CalledOnValidThread());
|
| +
|
| + provisional_main_frame_load_ = false;
|
| + if (state_ == STATE_CAPTIVE_PORTAL_LOGIN_PAGE)
|
| + return;
|
| + SetState(STATE_NONE);
|
| +}
|
| +
|
| +void CaptivePortalTabHelper::OnStopLoading() {
|
| + DCHECK(CalledOnValidThread());
|
| +
|
| + // Check for captive portal when loading stops. This can't be done on
|
| + // DidFinishLoad in the case of captive portals that return the user to
|
| + // the originally requested page, as getting a 204 response does not result
|
| + // in a DidFinishLoad call.
|
| + if (state_ == STATE_CAPTIVE_PORTAL_LOGIN_PAGE)
|
| + CheckForCaptivePortal();
|
| +}
|
| +
|
| +void CaptivePortalTabHelper::Observe(
|
| + int type,
|
| + const content::NotificationSource& source,
|
| + const content::NotificationDetails& details) {
|
| + DCHECK(CalledOnValidThread());
|
| + DCHECK_EQ(chrome::NOTIFICATION_CAPTIVE_PORTAL_CHECK_RESULT, type);
|
| + DCHECK_EQ(profile_, content::Source<Profile>(source).ptr());
|
| +
|
| + CaptivePortalService::Results* results =
|
| + content::Details<CaptivePortalService::Results>(details).ptr();
|
| +
|
| + DCHECK(CalledOnValidThread());
|
| +
|
| + if (results->result == RESULT_BEHIND_CAPTIVE_PORTAL) {
|
| + if (state_ == STATE_MAYBE_BROKEN_BY_PORTAL) {
|
| + SetState(STATE_BROKEN_BY_PORTAL);
|
| + MaybeOpenCaptivePortalLoginTab();
|
| + }
|
| + return;
|
| + }
|
| +
|
| + switch (state_) {
|
| + case STATE_MAYBE_BROKEN_BY_PORTAL:
|
| + case STATE_TIMER_RUNNING:
|
| + // If the previous result was BEHIND_CAPTIVE_PORTAL, and the state is
|
| + // either STATE_MAYBE_BROKEN_BY_PORTAL or STATE_TIMER_RUNNING, reload the
|
| + // tab. In the latter case, the tab has yet to commit, but is an SSL
|
| + // page, so if the page ends up at a timeout error, it will be reloaded.
|
| + // If not, the state will just be reset. The helps in the case that a
|
| + // user tries to reload a tab, and then quickly logs in.
|
| + if (results->previous_result == RESULT_BEHIND_CAPTIVE_PORTAL) {
|
| + SetState(STATE_NEEDS_RELOAD);
|
| + return;
|
| + }
|
| + SetState(STATE_NONE);
|
| + return;
|
| +
|
| + case STATE_BROKEN_BY_PORTAL:
|
| + // Either reload the tab now, if a connection timed out error page has
|
| + // already been committed, or reload it if and when a timeout commits.
|
| + SetState(STATE_NEEDS_RELOAD);
|
| + return;
|
| +
|
| + case STATE_CAPTIVE_PORTAL_LOGIN_PAGE:
|
| + // There is no captive portal any more, so this is no longer a login tab.
|
| + SetState(STATE_NONE);
|
| + return;
|
| +
|
| + case STATE_NEEDS_RELOAD:
|
| + case STATE_NONE:
|
| + // If the tab needs to reload or is in STATE_NONE, do nothing. The reload
|
| + // case shouldn't be very common, since it only lasts until a tab times
|
| + // out, but it's still possible.
|
| + return;
|
| +
|
| + default:
|
| + NOTREACHED();
|
| + }
|
| +}
|
| +
|
| +void CaptivePortalTabHelper::OnSlowSSLConnect() {
|
| + SetState(STATE_MAYBE_BROKEN_BY_PORTAL);
|
| +}
|
| +
|
| +void CaptivePortalTabHelper::SetState(State new_state) {
|
| + // Stop the timer even when old and new states are the same.
|
| + if (state_ == STATE_TIMER_RUNNING) {
|
| + slow_ssl_load_timer_.Stop();
|
| + } else {
|
| + DCHECK(!slow_ssl_load_timer_.IsRunning());
|
| + }
|
| +
|
| + // Check for unexpected state transitions.
|
| + switch (state_) {
|
| + case STATE_NONE:
|
| + DCHECK(new_state == STATE_NONE ||
|
| + new_state == STATE_TIMER_RUNNING ||
|
| + new_state == STATE_CAPTIVE_PORTAL_LOGIN_PAGE);
|
| + break;
|
| + case STATE_CAPTIVE_PORTAL_LOGIN_PAGE:
|
| + DCHECK_EQ(STATE_NONE, new_state);
|
| + break;
|
| + case STATE_TIMER_RUNNING:
|
| + DCHECK(new_state == STATE_NONE ||
|
| + new_state == STATE_MAYBE_BROKEN_BY_PORTAL ||
|
| + new_state == STATE_NEEDS_RELOAD);
|
| + break;
|
| + case STATE_MAYBE_BROKEN_BY_PORTAL:
|
| + DCHECK(new_state == STATE_NONE ||
|
| + new_state == STATE_BROKEN_BY_PORTAL ||
|
| + new_state == STATE_NEEDS_RELOAD);
|
| + break;
|
| + case STATE_BROKEN_BY_PORTAL:
|
| + DCHECK(new_state == STATE_NONE ||
|
| + new_state == STATE_NEEDS_RELOAD);
|
| + break;
|
| + case STATE_NEEDS_RELOAD:
|
| + DCHECK_EQ(STATE_NONE, new_state);
|
| + break;
|
| + default:
|
| + NOTREACHED();
|
| + break;
|
| + };
|
| +
|
| + state_ = new_state;
|
| +
|
| + switch (state_) {
|
| + case STATE_TIMER_RUNNING:
|
| + slow_ssl_load_timer_.Start(
|
| + FROM_HERE,
|
| + slow_ssl_load_time_,
|
| + this,
|
| + &CaptivePortalTabHelper::OnSlowSSLConnect);
|
| + break;
|
| +
|
| + case STATE_MAYBE_BROKEN_BY_PORTAL:
|
| + CheckForCaptivePortal();
|
| + break;
|
| +
|
| + case STATE_NEEDS_RELOAD:
|
| + // Try to reload the tab now.
|
| + ReloadTabIfNeeded();
|
| + break;
|
| +
|
| + default:
|
| + break;
|
| + }
|
| +}
|
| +
|
| +void CaptivePortalTabHelper::ReloadTabIfNeeded() {
|
| + // If there's still a provisional load going, or the page no longer needs
|
| + // to be reloaded, due to a new navigation, do nothing.
|
| + if (state_ != STATE_NEEDS_RELOAD || provisional_main_frame_load_)
|
| + return;
|
| + SetState(STATE_NONE);
|
| + ReloadTab();
|
| +}
|
| +
|
| +void CaptivePortalTabHelper::ReloadTab() {
|
| + content::NavigationController* controller =
|
| + &observer_.web_contents()->GetController();
|
| + if (!controller->GetActiveEntry()->GetHasPostData())
|
| + observer_.web_contents()->GetController().Reload(true);
|
| +}
|
| +
|
| +void CaptivePortalTabHelper::MaybeOpenCaptivePortalLoginTab() {
|
| + Browser* browser = browser::FindTabbedBrowser(profile_, true);
|
| + // 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_count(); ++i) {
|
| + TabContentsWrapper* tab_contents_wrapper =
|
| + browser->GetTabContentsWrapperAt(i);
|
| + if (tab_contents_wrapper->captive_portal_tab_helper()->state() ==
|
| + CaptivePortalTabHelper::STATE_CAPTIVE_PORTAL_LOGIN_PAGE) {
|
| + 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.
|
| + TabContentsWrapper* tab_contents_wrapper =
|
| + browser->AddSelectedTabWithURL(
|
| + CaptivePortalServiceFactory::GetForProfile(profile_)->test_url(),
|
| + content::PAGE_TRANSITION_TYPED);
|
| + tab_contents_wrapper->captive_portal_tab_helper()->SetState(
|
| + STATE_CAPTIVE_PORTAL_LOGIN_PAGE);
|
| +}
|
| +
|
| +void CaptivePortalTabHelper::CheckForCaptivePortal() {
|
| + CaptivePortalService* service =
|
| + CaptivePortalServiceFactory::GetForProfile(profile_);
|
| + if (service)
|
| + service->DetectCaptivePortal();
|
| +}
|
| +
|
| +} // namespace captive_portal
|
|
|
| Property changes on: chrome\browser\captive_portal\captive_portal_tab_helper.cc
|
| ___________________________________________________________________
|
| Added: svn:eol-style
|
| + LF
|
|
|
|
|