blob: 1d97190ca66b53e226e0e7726d8c5a54a9e36cb1 [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/ui/omnibox/alternate_nav_url_fetcher.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/infobars/infobar_service.h"
#include "chrome/browser/intranet_redirect_detector.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/omnibox/alternate_nav_infobar_delegate.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/notification_service.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/load_flags.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#include "net/url_request/url_fetcher.h"
#include "net/url_request/url_request.h"
using content::NavigationController;
AlternateNavURLFetcher::AlternateNavURLFetcher(
const GURL& alternate_nav_url)
: alternate_nav_url_(alternate_nav_url),
controller_(NULL),
state_(NOT_STARTED),
navigated_to_entry_(false) {
registrar_.Add(this, content::NOTIFICATION_NAV_ENTRY_PENDING,
content::NotificationService::AllSources());
}
AlternateNavURLFetcher::~AlternateNavURLFetcher() {
}
void AlternateNavURLFetcher::Observe(
int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) {
switch (type) {
case content::NOTIFICATION_NAV_ENTRY_PENDING: {
// If we've already received a notification for the same controller, we
// should delete ourselves as that indicates that the page is being
// re-loaded so this instance is now stale.
NavigationController* controller =
content::Source<NavigationController>(source).ptr();
if (controller_ == controller) {
delete this;
} else if (!controller_) {
// Start listening for the commit notification.
DCHECK(controller->GetPendingEntry());
registrar_.Add(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED,
content::Source<NavigationController>(
controller));
StartFetch(controller);
}
break;
}
case content::NOTIFICATION_NAV_ENTRY_COMMITTED:
// The page was navigated, we can show the infobar now if necessary.
registrar_.Remove(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED,
content::Source<NavigationController>(controller_));
navigated_to_entry_ = true;
ShowInfobarIfPossible();
// WARNING: |this| may be deleted!
break;
case content::NOTIFICATION_WEB_CONTENTS_DESTROYED:
// We have been closed. In order to prevent the URLFetcher from trying to
// access the controller that will be invalid, we delete ourselves.
// This deletes the URLFetcher and insures its callback won't be called.
delete this;
break;
default:
NOTREACHED();
}
}
void AlternateNavURLFetcher::OnURLFetchComplete(
const net::URLFetcher* source) {
DCHECK_EQ(fetcher_.get(), source);
SetStatusFromURLFetch(
source->GetURL(), source->GetStatus(), source->GetResponseCode());
ShowInfobarIfPossible();
// WARNING: |this| may be deleted!
}
void AlternateNavURLFetcher::StartFetch(NavigationController* controller) {
controller_ = controller;
content::WebContents* web_contents = controller_->GetWebContents();
registrar_.Add(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
content::Source<content::WebContents>(web_contents));
DCHECK_EQ(NOT_STARTED, state_);
state_ = IN_PROGRESS;
fetcher_.reset(net::URLFetcher::Create(GURL(alternate_nav_url_),
net::URLFetcher::HEAD, this));
fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES);
fetcher_->SetRequestContext(
controller_->GetBrowserContext()->GetRequestContext());
fetcher_->SetStopOnRedirect(true);
fetcher_->Start();
}
void AlternateNavURLFetcher::SetStatusFromURLFetch(
const GURL& url,
const net::URLRequestStatus& status,
int response_code) {
if (status.is_success()) {
// HTTP 2xx, 401, and 407 all indicate that the target address exists.
state_ = (((response_code / 100) == 2) || (response_code == 401) ||
(response_code == 407)) ? SUCCEEDED : FAILED;
} else {
// If we got HTTP 3xx, we'll have auto-canceled; treat this as evidence the
// target address exists as long as we're not redirected to a common
// location every time, lest we annoy the user with infobars on everything
// they type. See comments on IntranetRedirectDetector.
if (status.status() == net::URLRequestStatus::CANCELED &&
(response_code / 100) == 3) {
const bool same_domain_or_host =
net::registry_controlled_domains::SameDomainOrHost(
url,
IntranetRedirectDetector::RedirectOrigin(),
net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES);
state_ = same_domain_or_host ? FAILED : SUCCEEDED;
} else {
state_ = FAILED;
}
}
}
void AlternateNavURLFetcher::ShowInfobarIfPossible() {
if (navigated_to_entry_ && (state_ == SUCCEEDED)) {
AlternateNavInfoBarDelegate::Create(
InfoBarService::FromWebContents(controller_->GetWebContents()),
alternate_nav_url_);
} else if (state_ != FAILED) {
return;
}
delete this;
}