1// Copyright (c) 2012 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/managed_mode/managed_mode_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/confirm_infobar_delegate.h"
15#include "chrome/browser/infobars/infobar.h"
16#include "chrome/browser/infobars/infobar_service.h"
17#include "chrome/browser/managed_mode/managed_mode_interstitial.h"
18#include "chrome/browser/managed_mode/managed_mode_resource_throttle.h"
19#include "chrome/browser/managed_mode/managed_mode_url_filter.h"
20#include "chrome/browser/managed_mode/managed_user_service.h"
21#include "chrome/browser/managed_mode/managed_user_service_factory.h"
22#include "chrome/browser/profiles/profile.h"
23#include "chrome/browser/tab_contents/tab_util.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 "content/public/browser/web_contents_view.h"
30#include "grit/generated_resources.h"
31#include "ui/base/l10n/l10n_util.h"
32
33#if !defined(OS_ANDROID)
34#include "chrome/browser/ui/browser.h"
35#include "chrome/browser/ui/browser_commands.h"
36#include "chrome/browser/ui/browser_finder.h"
37#include "chrome/browser/ui/browser_list.h"
38#include "chrome/browser/ui/host_desktop.h"
39#include "chrome/browser/ui/tabs/tab_strip_model.h"
40#endif
41
42using base::Time;
43using content::NavigationEntry;
44
45namespace {
46
47
48// Helpers --------------------------------------------------------------------
49
50#if !defined(OS_ANDROID)
51// TODO(bauerb): Get rid of the platform-specific #ifdef here.
52// http://crbug.com/313377
53void GoBackToSafety(content::WebContents* web_contents) {
54  // For now, just go back one page (the user didn't retreat from that page,
55  // so it should be okay).
56  content::NavigationController* controller =
57      &web_contents->GetController();
58  if (controller->CanGoBack()) {
59    controller->GoBack();
60    return;
61  }
62
63  // If we can't go back (because we opened a new tab), try to close the tab.
64  // If this is the last tab on this desktop, open a new window.
65  chrome::HostDesktopType host_desktop_type =
66      chrome::GetHostDesktopTypeForNativeView(
67          web_contents->GetView()->GetNativeView());
68  const BrowserList* browser_list = BrowserList::GetInstance(host_desktop_type);
69  if (browser_list->size() == 1) {
70    Browser* browser = browser_list->get(0);
71    DCHECK(browser == chrome::FindBrowserWithWebContents(web_contents));
72    if (browser->tab_strip_model()->count() == 1)
73      chrome::NewEmptyWindow(browser->profile(), browser->host_desktop_type());
74  }
75
76  web_contents->GetDelegate()->CloseContents(web_contents);
77}
78#endif
79
80// ManagedModeWarningInfoBarDelegate ------------------------------------------
81
82class ManagedModeWarningInfoBarDelegate : public ConfirmInfoBarDelegate {
83 public:
84  // Creates a managed mode warning infobar and delegate and adds the infobar to
85  // |infobar_service|.  Returns the infobar if it was successfully added.
86  static InfoBar* Create(InfoBarService* infobar_service);
87
88 private:
89  ManagedModeWarningInfoBarDelegate();
90  virtual ~ManagedModeWarningInfoBarDelegate();
91
92  // ConfirmInfoBarDelegate:
93  virtual bool ShouldExpire(
94      const content::LoadCommittedDetails& details) const OVERRIDE;
95  virtual void InfoBarDismissed() OVERRIDE;
96  virtual base::string16 GetMessageText() const OVERRIDE;
97  virtual int GetButtons() const OVERRIDE;
98  virtual base::string16 GetButtonLabel(InfoBarButton button) const OVERRIDE;
99  virtual bool Accept() OVERRIDE;
100
101  DISALLOW_COPY_AND_ASSIGN(ManagedModeWarningInfoBarDelegate);
102};
103
104// static
105InfoBar* ManagedModeWarningInfoBarDelegate::Create(
106    InfoBarService* infobar_service) {
107  return infobar_service->AddInfoBar(ConfirmInfoBarDelegate::CreateInfoBar(
108      scoped_ptr<ConfirmInfoBarDelegate>(
109          new ManagedModeWarningInfoBarDelegate())));
110}
111
112ManagedModeWarningInfoBarDelegate::ManagedModeWarningInfoBarDelegate()
113    : ConfirmInfoBarDelegate() {
114}
115
116ManagedModeWarningInfoBarDelegate::~ManagedModeWarningInfoBarDelegate() {
117}
118
119bool ManagedModeWarningInfoBarDelegate::ShouldExpire(
120    const content::LoadCommittedDetails& details) const {
121  // ManagedModeNavigationObserver removes us below.
122  return false;
123}
124
125void ManagedModeWarningInfoBarDelegate::InfoBarDismissed() {
126  ManagedModeNavigationObserver::FromWebContents(
127      web_contents())->WarnInfoBarDismissed();
128}
129
130base::string16 ManagedModeWarningInfoBarDelegate::GetMessageText() const {
131  return l10n_util::GetStringUTF16(IDS_MANAGED_USER_WARN_INFOBAR_MESSAGE);
132}
133
134int ManagedModeWarningInfoBarDelegate::GetButtons() const {
135  return BUTTON_OK;
136}
137
138base::string16 ManagedModeWarningInfoBarDelegate::GetButtonLabel(
139    InfoBarButton button) const {
140  DCHECK_EQ(BUTTON_OK, button);
141  return l10n_util::GetStringUTF16(IDS_MANAGED_USER_WARN_INFOBAR_GO_BACK);
142}
143
144bool ManagedModeWarningInfoBarDelegate::Accept() {
145#if defined(OS_ANDROID)
146  // TODO(bauerb): Get rid of the platform-specific #ifdef here.
147  // http://crbug.com/313377
148  NOTIMPLEMENTED();
149#else
150  GoBackToSafety(web_contents());
151#endif
152
153  return false;
154}
155
156
157}  // namespace
158
159// ManagedModeNavigationObserver ----------------------------------------------
160
161DEFINE_WEB_CONTENTS_USER_DATA_KEY(ManagedModeNavigationObserver);
162
163ManagedModeNavigationObserver::~ManagedModeNavigationObserver() {
164}
165
166ManagedModeNavigationObserver::ManagedModeNavigationObserver(
167    content::WebContents* web_contents)
168    : WebContentsObserver(web_contents),
169      warn_infobar_(NULL) {
170  Profile* profile =
171      Profile::FromBrowserContext(web_contents->GetBrowserContext());
172  managed_user_service_ = ManagedUserServiceFactory::GetForProfile(profile);
173  url_filter_ = managed_user_service_->GetURLFilterForUIThread();
174}
175
176void ManagedModeNavigationObserver::WarnInfoBarDismissed() {
177  DCHECK(warn_infobar_);
178  warn_infobar_ = NULL;
179}
180
181void ManagedModeNavigationObserver::ProvisionalChangeToMainFrameUrl(
182    const GURL& url,
183    content::RenderViewHost* render_view_host) {
184  ManagedModeURLFilter::FilteringBehavior behavior =
185      url_filter_->GetFilteringBehaviorForURL(url);
186
187  if (behavior == ManagedModeURLFilter::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 ManagedModeNavigationObserver::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  ManagedModeURLFilter::FilteringBehavior behavior =
207      url_filter_->GetFilteringBehaviorForURL(url);
208
209  if (behavior == ManagedModeURLFilter::WARN && !warn_infobar_) {
210    warn_infobar_ = ManagedModeWarningInfoBarDelegate::Create(
211        InfoBarService::FromWebContents(web_contents()));
212  }
213}
214
215// static
216void ManagedModeNavigationObserver::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  ManagedModeNavigationObserver* navigation_observer =
230      ManagedModeNavigationObserver::FromWebContents(web_contents);
231  if (navigation_observer)
232    navigation_observer->OnRequestBlockedInternal(url);
233
234  // Show the interstitial.
235  new ManagedModeInterstitial(web_contents, url, callback);
236}
237
238void ManagedModeNavigationObserver::OnRequestBlockedInternal(const GURL& url) {
239  Time timestamp = Time::Now();  // TODO(bauerb): Use SaneTime when available.
240  // Create a history entry for the attempt and mark it as such.
241  history::HistoryAddPageArgs add_page_args(
242        url, timestamp, web_contents(), 0,
243        url, history::RedirectList(),
244        content::PAGE_TRANSITION_BLOCKED, history::SOURCE_BROWSED,
245        false);
246
247  // Add the entry to the history database.
248  Profile* profile =
249      Profile::FromBrowserContext(web_contents()->GetBrowserContext());
250  HistoryService* history_service =
251     HistoryServiceFactory::GetForProfile(profile, Profile::IMPLICIT_ACCESS);
252
253  // |history_service| is null if saving history is disabled.
254  if (history_service)
255    history_service->AddPage(add_page_args);
256
257  scoped_ptr<NavigationEntry> entry(NavigationEntry::Create());
258  entry->SetVirtualURL(url);
259  entry->SetTimestamp(timestamp);
260  blocked_navigations_.push_back(entry.release());
261  ManagedUserService* managed_user_service =
262      ManagedUserServiceFactory::GetForProfile(profile);
263  managed_user_service->DidBlockNavigation(web_contents());
264}
265