| // Copyright (c) 2011 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/alternate_nav_url_fetcher.h" |
| |
| #include "base/utf_string_conversions.h" |
| #include "chrome/browser/intranet_redirect_detector.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "content/browser/tab_contents/navigation_controller.h" |
| #include "content/browser/tab_contents/navigation_entry.h" |
| #include "content/browser/tab_contents/tab_contents.h" |
| #include "content/common/notification_service.h" |
| #include "grit/generated_resources.h" |
| #include "grit/theme_resources.h" |
| #include "net/base/registry_controlled_domain.h" |
| #include "net/url_request/url_request.h" |
| #include "ui/base/l10n/l10n_util.h" |
| #include "ui/base/resource/resource_bundle.h" |
| |
| AlternateNavURLFetcher::AlternateNavURLFetcher( |
| const GURL& alternate_nav_url) |
| : LinkInfoBarDelegate(NULL), |
| alternate_nav_url_(alternate_nav_url), |
| controller_(NULL), |
| state_(NOT_STARTED), |
| navigated_to_entry_(false), |
| infobar_contents_(NULL) { |
| registrar_.Add(this, NotificationType::NAV_ENTRY_PENDING, |
| NotificationService::AllSources()); |
| } |
| |
| AlternateNavURLFetcher::~AlternateNavURLFetcher() {} |
| |
| void AlternateNavURLFetcher::Observe(NotificationType type, |
| const NotificationSource& source, |
| const NotificationDetails& details) { |
| switch (type.value) { |
| case NotificationType::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. |
| // http://crbug.com/43378 |
| if (!infobar_contents_ && |
| controller_ == Source<NavigationController>(source).ptr()) { |
| delete this; |
| } else if (!controller_) { |
| controller_ = Source<NavigationController>(source).ptr(); |
| DCHECK(controller_->pending_entry()); |
| |
| // Start listening for the commit notification. We also need to listen |
| // for the tab close command since that means the load will never |
| // commit! |
| registrar_.Add(this, NotificationType::NAV_ENTRY_COMMITTED, |
| Source<NavigationController>(controller_)); |
| registrar_.Add(this, NotificationType::TAB_CLOSED, |
| Source<NavigationController>(controller_)); |
| |
| DCHECK_EQ(NOT_STARTED, state_); |
| state_ = IN_PROGRESS; |
| fetcher_.reset(new URLFetcher(GURL(alternate_nav_url_), |
| URLFetcher::HEAD, this)); |
| fetcher_->set_request_context( |
| controller_->profile()->GetRequestContext()); |
| fetcher_->Start(); |
| } |
| break; |
| |
| case NotificationType::NAV_ENTRY_COMMITTED: |
| // The page was navigated, we can show the infobar now if necessary. |
| registrar_.Remove(this, NotificationType::NAV_ENTRY_COMMITTED, |
| Source<NavigationController>(controller_)); |
| navigated_to_entry_ = true; |
| ShowInfobarIfPossible(); |
| break; |
| |
| case NotificationType::TAB_CLOSED: |
| // 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 URLFetcher* source, |
| const GURL& url, |
| const net::URLRequestStatus& status, |
| int response_code, |
| const ResponseCookies& cookies, |
| const std::string& data) { |
| DCHECK_EQ(fetcher_.get(), source); |
| SetStatusFromURLFetch(url, status, response_code); |
| ShowInfobarIfPossible(); |
| } |
| |
| SkBitmap* AlternateNavURLFetcher::GetIcon() const { |
| return ResourceBundle::GetSharedInstance().GetBitmapNamed( |
| IDR_INFOBAR_ALT_NAV_URL); |
| } |
| |
| InfoBarDelegate::Type AlternateNavURLFetcher::GetInfoBarType() const { |
| return PAGE_ACTION_TYPE; |
| } |
| |
| string16 AlternateNavURLFetcher::GetMessageTextWithOffset( |
| size_t* link_offset) const { |
| const string16 label = l10n_util::GetStringFUTF16( |
| IDS_ALTERNATE_NAV_URL_VIEW_LABEL, string16(), link_offset); |
| return label; |
| } |
| |
| string16 AlternateNavURLFetcher::GetLinkText() const { |
| return UTF8ToUTF16(alternate_nav_url_.spec()); |
| } |
| |
| bool AlternateNavURLFetcher::LinkClicked(WindowOpenDisposition disposition) { |
| infobar_contents_->OpenURL( |
| alternate_nav_url_, GURL(), disposition, |
| // Pretend the user typed this URL, so that navigating to |
| // it will be the default action when it's typed again in |
| // the future. |
| PageTransition::TYPED); |
| |
| // We should always close, even if the navigation did not occur within this |
| // TabContents. |
| return true; |
| } |
| |
| void AlternateNavURLFetcher::InfoBarClosed() { |
| delete this; |
| } |
| |
| 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. |
| (((response_code / 100) != 2) && |
| (response_code != 401) && (response_code != 407)) || |
| // Fail if we're redirected to a common location. |
| // This happens for ISPs/DNS providers/etc. who return |
| // provider-controlled pages to arbitrary user navigation attempts. |
| // Because this can result in infobars on large fractions of user |
| // searches, we don't show automatic infobars for these. Note that users |
| // can still choose to explicitly navigate to or search for pages in |
| // these domains, and can still get infobars for cases that wind up on |
| // other domains (e.g. legit intranet sites), we're just trying to avoid |
| // erroneously harassing the user with our own UI prompts. |
| net::RegistryControlledDomainService::SameDomainOrHost(url, |
| IntranetRedirectDetector::RedirectOrigin())) { |
| state_ = FAILED; |
| return; |
| } |
| state_ = SUCCEEDED; |
| } |
| |
| void AlternateNavURLFetcher::ShowInfobarIfPossible() { |
| if (!navigated_to_entry_ || state_ != SUCCEEDED) { |
| if (state_ == FAILED) |
| delete this; |
| return; |
| } |
| |
| infobar_contents_ = controller_->tab_contents(); |
| StoreActiveEntryUniqueID(infobar_contents_); |
| infobar_contents_->AddInfoBar(this); |
| } |