supervised_user_navigation_observer.cc revision f8ee788a64d60abd8f2d742a5fdedde054ecd910
1// Copyright 2014 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/supervised_user/supervised_user_navigation_observer.h" 6 7#include "base/bind.h" 8#include "base/callback.h" 9#include "base/metrics/histogram.h" 10#include "base/strings/string_util.h" 11#include "chrome/browser/history/history_service.h" 12#include "chrome/browser/history/history_service_factory.h" 13#include "chrome/browser/history/history_types.h" 14#include "chrome/browser/infobars/infobar_service.h" 15#include "chrome/browser/profiles/profile.h" 16#include "chrome/browser/supervised_user/supervised_user_interstitial.h" 17#include "chrome/browser/supervised_user/supervised_user_resource_throttle.h" 18#include "chrome/browser/supervised_user/supervised_user_service.h" 19#include "chrome/browser/supervised_user/supervised_user_service_factory.h" 20#include "chrome/browser/supervised_user/supervised_user_url_filter.h" 21#include "chrome/browser/tab_contents/tab_util.h" 22#include "components/infobars/core/confirm_infobar_delegate.h" 23#include "components/infobars/core/infobar.h" 24#include "content/public/browser/browser_thread.h" 25#include "content/public/browser/navigation_entry.h" 26#include "content/public/browser/render_process_host.h" 27#include "content/public/browser/render_view_host.h" 28#include "content/public/browser/user_metrics.h" 29#include "grit/generated_resources.h" 30#include "ui/base/l10n/l10n_util.h" 31 32#if !defined(OS_ANDROID) 33#include "chrome/browser/ui/browser.h" 34#include "chrome/browser/ui/browser_commands.h" 35#include "chrome/browser/ui/browser_finder.h" 36#include "chrome/browser/ui/browser_list.h" 37#include "chrome/browser/ui/host_desktop.h" 38#include "chrome/browser/ui/tabs/tab_strip_model.h" 39#endif 40 41using base::Time; 42using content::NavigationEntry; 43 44namespace { 45 46 47// Helpers -------------------------------------------------------------------- 48 49#if !defined(OS_ANDROID) 50// TODO(bauerb): Get rid of the platform-specific #ifdef here. 51// http://crbug.com/313377 52void GoBackToSafety(content::WebContents* web_contents) { 53 // For now, just go back one page (the user didn't retreat from that page, 54 // so it should be okay). 55 content::NavigationController* controller = 56 &web_contents->GetController(); 57 if (controller->CanGoBack()) { 58 controller->GoBack(); 59 return; 60 } 61 62 // If we can't go back (because we opened a new tab), try to close the tab. 63 // If this is the last tab on this desktop, open a new window. 64 chrome::HostDesktopType host_desktop_type = 65 chrome::GetHostDesktopTypeForNativeView(web_contents->GetNativeView()); 66 const BrowserList* browser_list = BrowserList::GetInstance(host_desktop_type); 67 if (browser_list->size() == 1) { 68 Browser* browser = browser_list->get(0); 69 DCHECK(browser == chrome::FindBrowserWithWebContents(web_contents)); 70 if (browser->tab_strip_model()->count() == 1) 71 chrome::NewEmptyWindow(browser->profile(), browser->host_desktop_type()); 72 } 73 74 web_contents->GetDelegate()->CloseContents(web_contents); 75} 76#endif 77 78// SupervisedUserWarningInfoBarDelegate --------------------------------------- 79 80class SupervisedUserWarningInfoBarDelegate : public ConfirmInfoBarDelegate { 81 public: 82 // Creates a supervised user warning infobar and delegate and adds the infobar 83 // to |infobar_service|. Returns the infobar if it was successfully added. 84 static infobars::InfoBar* Create(InfoBarService* infobar_service); 85 86 private: 87 SupervisedUserWarningInfoBarDelegate(); 88 virtual ~SupervisedUserWarningInfoBarDelegate(); 89 90 // ConfirmInfoBarDelegate: 91 virtual bool ShouldExpire(const NavigationDetails& details) const OVERRIDE; 92 virtual void InfoBarDismissed() OVERRIDE; 93 virtual base::string16 GetMessageText() const OVERRIDE; 94 virtual int GetButtons() const OVERRIDE; 95 virtual base::string16 GetButtonLabel(InfoBarButton button) const OVERRIDE; 96 virtual bool Accept() OVERRIDE; 97 98 DISALLOW_COPY_AND_ASSIGN(SupervisedUserWarningInfoBarDelegate); 99}; 100 101// static 102infobars::InfoBar* SupervisedUserWarningInfoBarDelegate::Create( 103 InfoBarService* infobar_service) { 104 return infobar_service->AddInfoBar(ConfirmInfoBarDelegate::CreateInfoBar( 105 scoped_ptr<ConfirmInfoBarDelegate>( 106 new SupervisedUserWarningInfoBarDelegate()))); 107} 108 109SupervisedUserWarningInfoBarDelegate::SupervisedUserWarningInfoBarDelegate() 110 : ConfirmInfoBarDelegate() { 111} 112 113SupervisedUserWarningInfoBarDelegate::~SupervisedUserWarningInfoBarDelegate() { 114} 115 116bool SupervisedUserWarningInfoBarDelegate::ShouldExpire( 117 const NavigationDetails& details) const { 118 // SupervisedUserNavigationObserver removes us below. 119 return false; 120} 121 122void SupervisedUserWarningInfoBarDelegate::InfoBarDismissed() { 123 content::WebContents* web_contents = 124 InfoBarService::WebContentsFromInfoBar(infobar()); 125 SupervisedUserNavigationObserver::FromWebContents( 126 web_contents)->WarnInfoBarDismissed(); 127} 128 129base::string16 SupervisedUserWarningInfoBarDelegate::GetMessageText() const { 130 return l10n_util::GetStringUTF16(IDS_MANAGED_USER_WARN_INFOBAR_MESSAGE); 131} 132 133int SupervisedUserWarningInfoBarDelegate::GetButtons() const { 134 return BUTTON_OK; 135} 136 137base::string16 SupervisedUserWarningInfoBarDelegate::GetButtonLabel( 138 InfoBarButton button) const { 139 DCHECK_EQ(BUTTON_OK, button); 140 return l10n_util::GetStringUTF16(IDS_MANAGED_USER_WARN_INFOBAR_GO_BACK); 141} 142 143bool SupervisedUserWarningInfoBarDelegate::Accept() { 144#if defined(OS_ANDROID) 145 // TODO(bauerb): Get rid of the platform-specific #ifdef here. 146 // http://crbug.com/313377 147 NOTIMPLEMENTED(); 148#else 149 GoBackToSafety(InfoBarService::WebContentsFromInfoBar(infobar())); 150#endif 151 152 return false; 153} 154 155 156} // namespace 157 158// SupervisedUserNavigationObserver ------------------------------------------- 159 160DEFINE_WEB_CONTENTS_USER_DATA_KEY(SupervisedUserNavigationObserver); 161 162SupervisedUserNavigationObserver::~SupervisedUserNavigationObserver() { 163} 164 165SupervisedUserNavigationObserver::SupervisedUserNavigationObserver( 166 content::WebContents* web_contents) 167 : WebContentsObserver(web_contents), 168 warn_infobar_(NULL) { 169 Profile* profile = 170 Profile::FromBrowserContext(web_contents->GetBrowserContext()); 171 supervised_user_service_ = 172 SupervisedUserServiceFactory::GetForProfile(profile); 173 url_filter_ = supervised_user_service_->GetURLFilterForUIThread(); 174} 175 176void SupervisedUserNavigationObserver::WarnInfoBarDismissed() { 177 DCHECK(warn_infobar_); 178 warn_infobar_ = NULL; 179} 180 181void SupervisedUserNavigationObserver::ProvisionalChangeToMainFrameUrl( 182 const GURL& url, 183 content::RenderFrameHost* render_frame_host) { 184 SupervisedUserURLFilter::FilteringBehavior behavior = 185 url_filter_->GetFilteringBehaviorForURL(url); 186 187 if (behavior == SupervisedUserURLFilter::WARN || !warn_infobar_) 188 return; 189 190 // If we shouldn't have a warn infobar remove it here. 191 InfoBarService::FromWebContents(web_contents())->RemoveInfoBar(warn_infobar_); 192 warn_infobar_ = NULL; 193} 194 195void SupervisedUserNavigationObserver::DidCommitProvisionalLoadForFrame( 196 int64 frame_id, 197 const base::string16& frame_unique_name, 198 bool is_main_frame, 199 const GURL& url, 200 content::PageTransition transition_type, 201 content::RenderViewHost* render_view_host) { 202 if (!is_main_frame) 203 return; 204 205 DVLOG(1) << "DidCommitProvisionalLoadForFrame " << url.spec(); 206 SupervisedUserURLFilter::FilteringBehavior behavior = 207 url_filter_->GetFilteringBehaviorForURL(url); 208 209 if (behavior == SupervisedUserURLFilter::WARN && !warn_infobar_) { 210 warn_infobar_ = SupervisedUserWarningInfoBarDelegate::Create( 211 InfoBarService::FromWebContents(web_contents())); 212 } 213} 214 215// static 216void SupervisedUserNavigationObserver::OnRequestBlocked( 217 int render_process_host_id, 218 int render_view_id, 219 const GURL& url, 220 const base::Callback<void(bool)>& callback) { 221 content::WebContents* web_contents = 222 tab_util::GetWebContentsByID(render_process_host_id, render_view_id); 223 if (!web_contents) { 224 content::BrowserThread::PostTask( 225 content::BrowserThread::IO, FROM_HERE, base::Bind(callback, false)); 226 return; 227 } 228 229 SupervisedUserNavigationObserver* navigation_observer = 230 SupervisedUserNavigationObserver::FromWebContents(web_contents); 231 if (navigation_observer) 232 navigation_observer->OnRequestBlockedInternal(url); 233 234 // Show the interstitial. 235 SupervisedUserInterstitial::Show(web_contents, url, callback); 236} 237 238void SupervisedUserNavigationObserver::OnRequestBlockedInternal( 239 const GURL& url) { 240 Time timestamp = Time::Now(); // TODO(bauerb): Use SaneTime when available. 241 // Create a history entry for the attempt and mark it as such. 242 history::HistoryAddPageArgs add_page_args( 243 url, timestamp, web_contents(), 0, 244 url, history::RedirectList(), 245 content::PAGE_TRANSITION_BLOCKED, history::SOURCE_BROWSED, 246 false); 247 248 // Add the entry to the history database. 249 Profile* profile = 250 Profile::FromBrowserContext(web_contents()->GetBrowserContext()); 251 HistoryService* history_service = 252 HistoryServiceFactory::GetForProfile(profile, Profile::IMPLICIT_ACCESS); 253 254 // |history_service| is null if saving history is disabled. 255 if (history_service) 256 history_service->AddPage(add_page_args); 257 258 scoped_ptr<NavigationEntry> entry(NavigationEntry::Create()); 259 entry->SetVirtualURL(url); 260 entry->SetTimestamp(timestamp); 261 blocked_navigations_.push_back(entry.release()); 262 SupervisedUserService* supervised_user_service = 263 SupervisedUserServiceFactory::GetForProfile(profile); 264 supervised_user_service->DidBlockNavigation(web_contents()); 265} 266