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_interstitial.h" 6 7#include "base/memory/weak_ptr.h" 8#include "base/metrics/histogram.h" 9#include "base/prefs/pref_service.h" 10#include "base/strings/string_number_conversions.h" 11#include "base/strings/utf_string_conversions.h" 12#include "base/values.h" 13#include "chrome/browser/infobars/infobar_service.h" 14#include "chrome/browser/profiles/profile.h" 15#include "chrome/browser/supervised_user/supervised_user_service.h" 16#include "chrome/browser/supervised_user/supervised_user_service_factory.h" 17#include "chrome/common/pref_names.h" 18#include "chrome/grit/generated_resources.h" 19#include "components/infobars/core/infobar.h" 20#include "components/infobars/core/infobar_delegate.h" 21#include "content/public/browser/browser_thread.h" 22#include "content/public/browser/interstitial_page.h" 23#include "content/public/browser/navigation_controller.h" 24#include "content/public/browser/navigation_details.h" 25#include "content/public/browser/navigation_entry.h" 26#include "content/public/browser/web_contents.h" 27#include "content/public/browser/web_contents_user_data.h" 28#include "content/public/browser/web_ui.h" 29#include "grit/browser_resources.h" 30#include "ui/base/l10n/l10n_util.h" 31#include "ui/base/resource/resource_bundle.h" 32#include "ui/base/webui/jstemplate_builder.h" 33#include "ui/base/webui/web_ui_util.h" 34 35#if !defined(OS_ANDROID) 36#include "chrome/browser/ui/browser_finder.h" 37#include "chrome/browser/ui/tabs/tab_strip_model.h" 38#endif 39 40using content::BrowserThread; 41using content::WebContents; 42 43namespace { 44 45static const int kAvatarSize1x = 45; 46static const int kAvatarSize2x = 90; 47 48std::string BuildAvatarImageUrl(const std::string& url, int size) { 49 std::string result = url; 50 size_t slash = result.rfind('/'); 51 if (slash != std::string::npos) 52 result.insert(slash, "/s" + base::IntToString(size)); 53 return result; 54} 55 56class TabCloser : public content::WebContentsUserData<TabCloser> { 57 // To use, call TabCloser::CreateForWebContents. 58 private: 59 friend class content::WebContentsUserData<TabCloser>; 60 61 explicit TabCloser(WebContents* web_contents) 62 : web_contents_(web_contents), weak_ptr_factory_(this) { 63 BrowserThread::PostTask( 64 BrowserThread::UI, 65 FROM_HERE, 66 base::Bind(&TabCloser::CloseTabImpl, weak_ptr_factory_.GetWeakPtr())); 67 } 68 virtual ~TabCloser() {} 69 70 void CloseTabImpl() { 71 // On Android, FindBrowserWithWebContents and TabStripModel don't exist. 72#if !defined(OS_ANDROID) 73 Browser* browser = chrome::FindBrowserWithWebContents(web_contents_); 74 DCHECK(browser); 75 TabStripModel* tab_strip = browser->tab_strip_model(); 76 DCHECK_NE(TabStripModel::kNoTab, 77 tab_strip->GetIndexOfWebContents(web_contents_)); 78 if (tab_strip->count() <= 1) { 79 // Don't close the last tab in the window. 80 web_contents_->RemoveUserData(UserDataKey()); 81 return; 82 } 83#endif 84 web_contents_->Close(); 85 } 86 87 WebContents* web_contents_; 88 base::WeakPtrFactory<TabCloser> weak_ptr_factory_; 89}; 90 91} // namespace 92 93DEFINE_WEB_CONTENTS_USER_DATA_KEY(TabCloser); 94 95// static 96void SupervisedUserInterstitial::Show( 97 WebContents* web_contents, 98 const GURL& url, 99 const base::Callback<void(bool)>& callback) { 100 SupervisedUserInterstitial* interstitial = 101 new SupervisedUserInterstitial(web_contents, url, callback); 102 103 // If Init() does not complete fully, immediately delete the interstitial. 104 if (!interstitial->Init()) 105 delete interstitial; 106 // Otherwise |interstitial_page_| is responsible for deleting it. 107} 108 109SupervisedUserInterstitial::SupervisedUserInterstitial( 110 WebContents* web_contents, 111 const GURL& url, 112 const base::Callback<void(bool)>& callback) 113 : web_contents_(web_contents), 114 profile_(Profile::FromBrowserContext(web_contents->GetBrowserContext())), 115 interstitial_page_(NULL), 116 url_(url), 117 callback_(callback) {} 118 119SupervisedUserInterstitial::~SupervisedUserInterstitial() { 120 DCHECK(!web_contents_); 121} 122 123bool SupervisedUserInterstitial::Init() { 124 if (ShouldProceed()) { 125 // It can happen that the site was only allowed very recently and the URL 126 // filter on the IO thread had not been updated yet. Proceed with the 127 // request without showing the interstitial. 128 DispatchContinueRequest(true); 129 return false; 130 } 131 132 InfoBarService* service = InfoBarService::FromWebContents(web_contents_); 133 if (service) { 134 // Remove all the infobars which are attached to |web_contents_| and for 135 // which ShouldExpire() returns true. 136 content::LoadCommittedDetails details; 137 // |details.is_in_page| is default false, and |details.is_main_frame| is 138 // default true. This results in is_navigation_to_different_page() returning 139 // true. 140 DCHECK(details.is_navigation_to_different_page()); 141 const content::NavigationController& controller = 142 web_contents_->GetController(); 143 details.entry = controller.GetActiveEntry(); 144 if (controller.GetLastCommittedEntry()) { 145 details.previous_entry_index = controller.GetLastCommittedEntryIndex(); 146 details.previous_url = controller.GetLastCommittedEntry()->GetURL(); 147 } 148 details.type = content::NAVIGATION_TYPE_NEW_PAGE; 149 for (int i = service->infobar_count() - 1; i >= 0; --i) { 150 infobars::InfoBar* infobar = service->infobar_at(i); 151 if (infobar->delegate()->ShouldExpire( 152 InfoBarService::NavigationDetailsFromLoadCommittedDetails( 153 details))) 154 service->RemoveInfoBar(infobar); 155 } 156 } 157 158 SupervisedUserService* supervised_user_service = 159 SupervisedUserServiceFactory::GetForProfile(profile_); 160 supervised_user_service->AddObserver(this); 161 162 interstitial_page_ = 163 content::InterstitialPage::Create(web_contents_, true, url_, this); 164 interstitial_page_->Show(); 165 166 return true; 167} 168 169std::string SupervisedUserInterstitial::GetHTMLContents() { 170 base::DictionaryValue strings; 171 strings.SetString("blockPageTitle", 172 l10n_util::GetStringUTF16(IDS_BLOCK_INTERSTITIAL_TITLE)); 173 174 SupervisedUserService* supervised_user_service = 175 SupervisedUserServiceFactory::GetForProfile(profile_); 176 177 bool allow_access_requests = supervised_user_service->AccessRequestsEnabled(); 178 strings.SetBoolean("allowAccessRequests", allow_access_requests); 179 180 std::string profile_image_url = profile_->GetPrefs()->GetString( 181 prefs::kSupervisedUserCustodianProfileImageURL); 182 strings.SetString("avatarURL1x", BuildAvatarImageUrl(profile_image_url, 183 kAvatarSize1x)); 184 strings.SetString("avatarURL2x", BuildAvatarImageUrl(profile_image_url, 185 kAvatarSize2x)); 186 187 std::string profile_image_url2 = profile_->GetPrefs()->GetString( 188 prefs::kSupervisedUserSecondCustodianProfileImageURL); 189 strings.SetString("secondAvatarURL1x", BuildAvatarImageUrl(profile_image_url2, 190 kAvatarSize1x)); 191 strings.SetString("secondAvatarURL2x", BuildAvatarImageUrl(profile_image_url2, 192 kAvatarSize2x)); 193 194 base::string16 custodian = 195 base::UTF8ToUTF16(supervised_user_service->GetCustodianName()); 196 strings.SetString( 197 "blockPageMessage", 198 allow_access_requests 199 ? l10n_util::GetStringFUTF16(IDS_BLOCK_INTERSTITIAL_MESSAGE, 200 custodian) 201 : l10n_util::GetStringUTF16( 202 IDS_BLOCK_INTERSTITIAL_MESSAGE_ACCESS_REQUESTS_DISABLED)); 203 204 strings.SetString("backButton", l10n_util::GetStringUTF16(IDS_BACK_BUTTON)); 205 strings.SetString( 206 "requestAccessButton", 207 l10n_util::GetStringUTF16(IDS_BLOCK_INTERSTITIAL_REQUEST_ACCESS_BUTTON)); 208 209 strings.SetString( 210 "requestSentMessage", 211 l10n_util::GetStringFUTF16(IDS_BLOCK_INTERSTITIAL_REQUEST_SENT_MESSAGE, 212 custodian)); 213 214 webui::SetFontAndTextDirection(&strings); 215 216 base::StringPiece html(ResourceBundle::GetSharedInstance().GetRawDataResource( 217 IDR_SUPERVISED_USER_BLOCK_INTERSTITIAL_HTML)); 218 219 webui::UseVersion2 version; 220 return webui::GetI18nTemplateHtml(html, &strings); 221} 222 223void SupervisedUserInterstitial::CommandReceived(const std::string& command) { 224 // For use in histograms. 225 enum Commands { 226 PREVIEW, 227 BACK, 228 NTP, 229 ACCESS_REQUEST, 230 HISTOGRAM_BOUNDING_VALUE 231 }; 232 233 if (command == "\"back\"") { 234 UMA_HISTOGRAM_ENUMERATION("ManagedMode.BlockingInterstitialCommand", 235 BACK, 236 HISTOGRAM_BOUNDING_VALUE); 237 238 // Close the tab if there is no history entry to go back to. 239 DCHECK(web_contents_->GetController().GetTransientEntry()); 240 if (web_contents_->GetController().GetEntryCount() == 1) 241 TabCloser::CreateForWebContents(web_contents_); 242 243 interstitial_page_->DontProceed(); 244 return; 245 } 246 247 if (command == "\"request\"") { 248 UMA_HISTOGRAM_ENUMERATION("ManagedMode.BlockingInterstitialCommand", 249 ACCESS_REQUEST, 250 HISTOGRAM_BOUNDING_VALUE); 251 252 SupervisedUserService* supervised_user_service = 253 SupervisedUserServiceFactory::GetForProfile(profile_); 254 supervised_user_service->AddAccessRequest(url_); 255 DVLOG(1) << "Sent access request for " << url_.spec(); 256 257 return; 258 } 259 260 NOTREACHED(); 261} 262 263void SupervisedUserInterstitial::OnProceed() { 264 // CHECK instead of DCHECK as defense in depth in case we'd accidentally 265 // proceed on a blocked page. 266 CHECK(ShouldProceed()); 267 DispatchContinueRequest(true); 268} 269 270void SupervisedUserInterstitial::OnDontProceed() { 271 DispatchContinueRequest(false); 272} 273 274void SupervisedUserInterstitial::OnURLFilterChanged() { 275 if (ShouldProceed()) 276 interstitial_page_->Proceed(); 277} 278 279bool SupervisedUserInterstitial::ShouldProceed() { 280 SupervisedUserService* supervised_user_service = 281 SupervisedUserServiceFactory::GetForProfile(profile_); 282 SupervisedUserURLFilter* url_filter = 283 supervised_user_service->GetURLFilterForUIThread(); 284 return url_filter->GetFilteringBehaviorForURL(url_) != 285 SupervisedUserURLFilter::BLOCK; 286} 287 288void SupervisedUserInterstitial::DispatchContinueRequest( 289 bool continue_request) { 290 SupervisedUserService* supervised_user_service = 291 SupervisedUserServiceFactory::GetForProfile(profile_); 292 supervised_user_service->RemoveObserver(this); 293 294 BrowserThread::PostTask( 295 BrowserThread::IO, FROM_HERE, base::Bind(callback_, continue_request)); 296 297 // After this, the WebContents may be destroyed. Make sure we don't try to use 298 // it again. 299 web_contents_ = NULL; 300} 301