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