supervised_user_navigation_observer.cc revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
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/infobars/infobar_service.h"
14#include "chrome/browser/profiles/profile.h"
15#include "chrome/browser/supervised_user/supervised_user_interstitial.h"
16#include "chrome/browser/supervised_user/supervised_user_resource_throttle.h"
17#include "chrome/browser/supervised_user/supervised_user_service.h"
18#include "chrome/browser/supervised_user/supervised_user_service_factory.h"
19#include "chrome/browser/supervised_user/supervised_user_url_filter.h"
20#include "chrome/browser/tab_contents/tab_util.h"
21#include "chrome/grit/generated_resources.h"
22#include "components/history/core/browser/history_types.h"
23#include "components/infobars/core/confirm_infobar_delegate.h"
24#include "components/infobars/core/infobar.h"
25#include "content/public/browser/browser_thread.h"
26#include "content/public/browser/navigation_entry.h"
27#include "content/public/browser/render_frame_host.h"
28#include "content/public/browser/render_process_host.h"
29#include "content/public/browser/user_metrics.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_SUPERVISED_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_SUPERVISED_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::DidCommitProvisionalLoadForFrame(
182    content::RenderFrameHost* render_frame_host,
183    const GURL& url,
184    ui::PageTransition transition_type) {
185  if (render_frame_host->GetParent())
186    return;
187
188  DVLOG(1) << "DidCommitProvisionalLoadForFrame " << url.spec();
189  SupervisedUserURLFilter::FilteringBehavior behavior =
190      url_filter_->GetFilteringBehaviorForURL(url);
191
192  if (behavior == SupervisedUserURLFilter::WARN && !warn_infobar_) {
193    warn_infobar_ = SupervisedUserWarningInfoBarDelegate::Create(
194        InfoBarService::FromWebContents(web_contents()));
195  } else if (behavior != SupervisedUserURLFilter::WARN && warn_infobar_) {
196    InfoBarService::FromWebContents(web_contents())->
197        RemoveInfoBar(warn_infobar_);
198    warn_infobar_ = NULL;
199  }
200}
201
202// static
203void SupervisedUserNavigationObserver::OnRequestBlocked(
204    int render_process_host_id,
205    int render_view_id,
206    const GURL& url,
207    const base::Callback<void(bool)>& callback) {
208  content::WebContents* web_contents =
209      tab_util::GetWebContentsByID(render_process_host_id, render_view_id);
210  if (!web_contents) {
211    content::BrowserThread::PostTask(
212        content::BrowserThread::IO, FROM_HERE, base::Bind(callback, false));
213    return;
214  }
215
216  SupervisedUserNavigationObserver* navigation_observer =
217      SupervisedUserNavigationObserver::FromWebContents(web_contents);
218  if (navigation_observer)
219    navigation_observer->OnRequestBlockedInternal(url);
220
221  // Show the interstitial.
222  SupervisedUserInterstitial::Show(web_contents, url, callback);
223}
224
225void SupervisedUserNavigationObserver::OnRequestBlockedInternal(
226    const GURL& url) {
227  Time timestamp = Time::Now();  // TODO(bauerb): Use SaneTime when available.
228  // Create a history entry for the attempt and mark it as such.
229  history::HistoryAddPageArgs add_page_args(
230        url, timestamp, web_contents(), 0,
231        url, history::RedirectList(),
232        ui::PAGE_TRANSITION_BLOCKED, history::SOURCE_BROWSED,
233        false);
234
235  // Add the entry to the history database.
236  Profile* profile =
237      Profile::FromBrowserContext(web_contents()->GetBrowserContext());
238  HistoryService* history_service =
239     HistoryServiceFactory::GetForProfile(profile, Profile::IMPLICIT_ACCESS);
240
241  // |history_service| is null if saving history is disabled.
242  if (history_service)
243    history_service->AddPage(add_page_args);
244
245  scoped_ptr<NavigationEntry> entry(NavigationEntry::Create());
246  entry->SetVirtualURL(url);
247  entry->SetTimestamp(timestamp);
248  blocked_navigations_.push_back(entry.release());
249  SupervisedUserService* supervised_user_service =
250      SupervisedUserServiceFactory::GetForProfile(profile);
251  supervised_user_service->DidBlockNavigation(web_contents());
252}
253