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 41 using base::Time; 42 using content::NavigationEntry; 43 44 namespace { 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 52 void 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 80 class 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 102 infobars::InfoBar* SupervisedUserWarningInfoBarDelegate::Create( 103 InfoBarService* infobar_service) { 104 return infobar_service->AddInfoBar(ConfirmInfoBarDelegate::CreateInfoBar( 105 scoped_ptr<ConfirmInfoBarDelegate>( 106 new SupervisedUserWarningInfoBarDelegate()))); 107 } 108 109 SupervisedUserWarningInfoBarDelegate::SupervisedUserWarningInfoBarDelegate() 110 : ConfirmInfoBarDelegate() { 111 } 112 113 SupervisedUserWarningInfoBarDelegate::~SupervisedUserWarningInfoBarDelegate() { 114 } 115 116 bool SupervisedUserWarningInfoBarDelegate::ShouldExpire( 117 const NavigationDetails& details) const { 118 // SupervisedUserNavigationObserver removes us below. 119 return false; 120 } 121 122 void SupervisedUserWarningInfoBarDelegate::InfoBarDismissed() { 123 content::WebContents* web_contents = 124 InfoBarService::WebContentsFromInfoBar(infobar()); 125 SupervisedUserNavigationObserver::FromWebContents( 126 web_contents)->WarnInfoBarDismissed(); 127 } 128 129 base::string16 SupervisedUserWarningInfoBarDelegate::GetMessageText() const { 130 return l10n_util::GetStringUTF16(IDS_MANAGED_USER_WARN_INFOBAR_MESSAGE); 131 } 132 133 int SupervisedUserWarningInfoBarDelegate::GetButtons() const { 134 return BUTTON_OK; 135 } 136 137 base::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 143 bool 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 160 DEFINE_WEB_CONTENTS_USER_DATA_KEY(SupervisedUserNavigationObserver); 161 162 SupervisedUserNavigationObserver::~SupervisedUserNavigationObserver() { 163 } 164 165 SupervisedUserNavigationObserver::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 176 void SupervisedUserNavigationObserver::WarnInfoBarDismissed() { 177 DCHECK(warn_infobar_); 178 warn_infobar_ = NULL; 179 } 180 181 void 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 195 void 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 216 void 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 238 void 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