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 <stddef.h>
8 
9 #include "base/bind.h"
10 #include "base/callback.h"
11 #include "base/callback_helpers.h"
12 #include "base/memory/ptr_util.h"
13 #include "base/memory/weak_ptr.h"
14 #include "base/metrics/histogram_macros.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "base/values.h"
18 #include "build/build_config.h"
19 #include "chrome/browser/browser_process.h"
20 #include "chrome/browser/infobars/infobar_service.h"
21 #include "chrome/browser/profiles/profile.h"
22 #include "chrome/browser/supervised_user/supervised_user_navigation_observer.h"
23 #include "chrome/browser/supervised_user/supervised_user_service.h"
24 #include "chrome/browser/supervised_user/supervised_user_service_factory.h"
25 #include "chrome/common/pref_names.h"
26 #include "chrome/grit/generated_resources.h"
27 #include "components/infobars/core/infobar.h"
28 #include "components/infobars/core/infobar_delegate.h"
29 #include "components/prefs/pref_service.h"
30 #include "content/public/browser/browser_task_traits.h"
31 #include "content/public/browser/browser_thread.h"
32 #include "content/public/browser/navigation_controller.h"
33 #include "content/public/browser/navigation_details.h"
34 #include "content/public/browser/navigation_entry.h"
35 #include "content/public/browser/web_contents.h"
36 #include "content/public/browser/web_contents_user_data.h"
37 #include "ui/base/l10n/l10n_util.h"
38 #include "ui/base/resource/resource_bundle.h"
39 
40 #if defined(OS_ANDROID)
41 #include "chrome/browser/supervised_user/child_accounts/child_account_feedback_reporter_android.h"
42 #else
43 #include "chrome/browser/ui/browser_finder.h"
44 #include "chrome/browser/ui/chrome_pages.h"
45 #include "chrome/browser/ui/tabs/tab_strip_model.h"
46 #endif
47 
48 using content::WebContents;
49 
50 namespace {
51 
52 // For use in histograms.
53 enum Commands { PREVIEW, BACK, NTP, ACCESS_REQUEST, HISTOGRAM_BOUNDING_VALUE };
54 
55 // For use in histograms.The enum values should remain synchronized with the
56 // enum ManagedUserURLRequestPermissionSource in
57 // tools/metrics/histograms/enums.xml.
58 enum class RequestPermissionSource {
59   MAIN_FRAME = 0,
60   SUB_FRAME,
61   HISTOGRAM_BOUNDING_VALUE
62 };
63 
64 class TabCloser : public content::WebContentsUserData<TabCloser> {
65  public:
~TabCloser()66   ~TabCloser() override {}
67 
MaybeClose(WebContents * web_contents)68   static void MaybeClose(WebContents* web_contents) {
69     DCHECK(web_contents);
70 
71     // Close the tab only if there is a browser for it (which is not the case
72     // for example in a <webview>).
73 #if !defined(OS_ANDROID)
74     if (!chrome::FindBrowserWithWebContents(web_contents))
75       return;
76 #endif
77     TabCloser::CreateForWebContents(web_contents);
78   }
79 
80  private:
81   friend class content::WebContentsUserData<TabCloser>;
82 
TabCloser(WebContents * web_contents)83   explicit TabCloser(WebContents* web_contents) : web_contents_(web_contents) {
84     content::GetUIThreadTaskRunner({})->PostTask(
85         FROM_HERE, base::BindOnce(&TabCloser::CloseTabImpl,
86                                   weak_ptr_factory_.GetWeakPtr()));
87   }
88 
CloseTabImpl()89   void CloseTabImpl() {
90     // On Android, FindBrowserWithWebContents and TabStripModel don't exist.
91 #if !defined(OS_ANDROID)
92     Browser* browser = chrome::FindBrowserWithWebContents(web_contents_);
93     DCHECK(browser);
94     TabStripModel* tab_strip = browser->tab_strip_model();
95     DCHECK_NE(TabStripModel::kNoTab,
96               tab_strip->GetIndexOfWebContents(web_contents_));
97     if (tab_strip->count() <= 1) {
98       // Don't close the last tab in the window.
99       web_contents_->RemoveUserData(UserDataKey());
100       return;
101     }
102 #endif
103     web_contents_->Close();
104   }
105 
106   WebContents* web_contents_;
107   base::WeakPtrFactory<TabCloser> weak_ptr_factory_{this};
108 
109   WEB_CONTENTS_USER_DATA_KEY_DECL();
110 
111   DISALLOW_COPY_AND_ASSIGN(TabCloser);
112 };
113 
WEB_CONTENTS_USER_DATA_KEY_IMPL(TabCloser)114 WEB_CONTENTS_USER_DATA_KEY_IMPL(TabCloser)
115 
116 // Removes all the infobars which are attached to |web_contents| and for
117 // which ShouldExpire() returns true.
118 void CleanUpInfoBar(content::WebContents* web_contents) {
119   InfoBarService* service = InfoBarService::FromWebContents(web_contents);
120   if (service) {
121     content::LoadCommittedDetails details;
122     // |details.is_same_document| is default false, and |details.is_main_frame|
123     // is default true. This results in is_navigation_to_different_page()
124     // returning true.
125     DCHECK(details.is_navigation_to_different_page());
126     content::NavigationController& controller = web_contents->GetController();
127     details.entry = controller.GetVisibleEntry();
128     if (controller.GetLastCommittedEntry()) {
129       details.previous_entry_index = controller.GetLastCommittedEntryIndex();
130       details.previous_url = controller.GetLastCommittedEntry()->GetURL();
131     }
132     details.type = content::NAVIGATION_TYPE_NEW_PAGE;
133     for (int i = service->infobar_count() - 1; i >= 0; --i) {
134       infobars::InfoBar* infobar = service->infobar_at(i);
135       if (infobar->delegate()->ShouldExpire(
136               InfoBarService::NavigationDetailsFromLoadCommittedDetails(
137                   details)))
138         service->RemoveInfoBar(infobar);
139     }
140   }
141 }
142 
143 }  // namespace
144 
145 // static
Create(WebContents * web_contents,const GURL & url,supervised_user_error_page::FilteringBehaviorReason reason,int frame_id,int64_t interstitial_navigation_id)146 std::unique_ptr<SupervisedUserInterstitial> SupervisedUserInterstitial::Create(
147     WebContents* web_contents,
148     const GURL& url,
149     supervised_user_error_page::FilteringBehaviorReason reason,
150     int frame_id,
151     int64_t interstitial_navigation_id) {
152   std::unique_ptr<SupervisedUserInterstitial> interstitial =
153       base::WrapUnique(new SupervisedUserInterstitial(
154           web_contents, url, reason, frame_id, interstitial_navigation_id));
155 
156   if (web_contents->GetMainFrame()->GetFrameTreeNodeId() == frame_id)
157     CleanUpInfoBar(web_contents);
158 
159   // Caller is responsible for deleting the interstitial.
160   return interstitial;
161 }
162 
SupervisedUserInterstitial(WebContents * web_contents,const GURL & url,supervised_user_error_page::FilteringBehaviorReason reason,int frame_id,int64_t interstitial_navigation_id)163 SupervisedUserInterstitial::SupervisedUserInterstitial(
164     WebContents* web_contents,
165     const GURL& url,
166     supervised_user_error_page::FilteringBehaviorReason reason,
167     int frame_id,
168     int64_t interstitial_navigation_id)
169     : web_contents_(web_contents),
170       profile_(Profile::FromBrowserContext(web_contents->GetBrowserContext())),
171       url_(url),
172       reason_(reason),
173       frame_id_(frame_id),
174       interstitial_navigation_id_(interstitial_navigation_id) {}
175 
~SupervisedUserInterstitial()176 SupervisedUserInterstitial::~SupervisedUserInterstitial() {}
177 
178 // static
GetHTMLContents(Profile * profile,supervised_user_error_page::FilteringBehaviorReason reason,bool already_sent_request,bool is_main_frame)179 std::string SupervisedUserInterstitial::GetHTMLContents(
180     Profile* profile,
181     supervised_user_error_page::FilteringBehaviorReason reason,
182     bool already_sent_request,
183     bool is_main_frame) {
184   bool is_child_account = profile->IsChild();
185 
186   bool is_deprecated = !is_child_account;
187 
188   SupervisedUserService* supervised_user_service =
189       SupervisedUserServiceFactory::GetForProfile(profile);
190 
191   std::string custodian = supervised_user_service->GetCustodianName();
192   std::string second_custodian =
193       supervised_user_service->GetSecondCustodianName();
194   std::string custodian_email =
195       supervised_user_service->GetCustodianEmailAddress();
196   std::string second_custodian_email =
197       supervised_user_service->GetSecondCustodianEmailAddress();
198   std::string profile_image_url = profile->GetPrefs()->GetString(
199       prefs::kSupervisedUserCustodianProfileImageURL);
200   std::string profile_image_url2 = profile->GetPrefs()->GetString(
201       prefs::kSupervisedUserSecondCustodianProfileImageURL);
202 
203   bool allow_access_requests = supervised_user_service->AccessRequestsEnabled();
204 
205   return supervised_user_error_page::BuildHtml(
206       allow_access_requests, profile_image_url, profile_image_url2, custodian,
207       custodian_email, second_custodian, second_custodian_email,
208       is_child_account, is_deprecated, reason,
209       g_browser_process->GetApplicationLocale(), already_sent_request,
210       is_main_frame);
211 }
212 
GoBack()213 void SupervisedUserInterstitial::GoBack() {
214   // GoBack only for main frame.
215   DCHECK_EQ(web_contents()->GetMainFrame()->GetFrameTreeNodeId(), frame_id());
216 
217   UMA_HISTOGRAM_ENUMERATION("ManagedMode.BlockingInterstitialCommand", BACK,
218                             HISTOGRAM_BOUNDING_VALUE);
219   AttemptMoveAwayFromCurrentFrameURL();
220   OnInterstitialDone();
221 }
222 
RequestPermission(base::OnceCallback<void (bool)> RequestCallback)223 void SupervisedUserInterstitial::RequestPermission(
224     base::OnceCallback<void(bool)> RequestCallback) {
225   UMA_HISTOGRAM_ENUMERATION("ManagedMode.BlockingInterstitialCommand",
226                             ACCESS_REQUEST, HISTOGRAM_BOUNDING_VALUE);
227 
228   RequestPermissionSource source;
229   if (web_contents()->GetMainFrame()->GetFrameTreeNodeId() == frame_id())
230     source = RequestPermissionSource::MAIN_FRAME;
231   else
232     source = RequestPermissionSource::SUB_FRAME;
233 
234   UMA_HISTOGRAM_ENUMERATION("ManagedUsers.RequestPermissionSource", source,
235                             RequestPermissionSource::HISTOGRAM_BOUNDING_VALUE);
236 
237   SupervisedUserService* supervised_user_service =
238       SupervisedUserServiceFactory::GetForProfile(profile_);
239   supervised_user_service->AddURLAccessRequest(url_,
240                                                std::move(RequestCallback));
241 }
242 
ShowFeedback()243 void SupervisedUserInterstitial::ShowFeedback() {
244   // TODO(yilkal): Remove checking IsChild since legacy supervised users are
245   // deprecated.
246   bool is_child_account = profile_->IsChild();
247 
248   SupervisedUserService* supervised_user_service =
249       SupervisedUserServiceFactory::GetForProfile(profile_);
250   std::string second_custodian =
251       supervised_user_service->GetSecondCustodianName();
252 
253   base::string16 reason =
254       l10n_util::GetStringUTF16(supervised_user_error_page::GetBlockMessageID(
255           reason_, is_child_account, second_custodian.empty()));
256   std::string message = l10n_util::GetStringFUTF8(
257       IDS_BLOCK_INTERSTITIAL_DEFAULT_FEEDBACK_TEXT, reason);
258 #if defined(OS_ANDROID)
259   DCHECK(is_child_account);
260   ReportChildAccountFeedback(web_contents_, message, url_);
261 #else
262   chrome::ShowFeedbackPage(
263       url_, profile_, chrome::kFeedbackSourceSupervisedUserInterstitial,
264       message, std::string() /* description_placeholder_text */,
265       std::string() /* category_tag */, std::string() /* extra_diagnostics */);
266 #endif
267   return;
268 }
269 
AttemptMoveAwayFromCurrentFrameURL()270 void SupervisedUserInterstitial::AttemptMoveAwayFromCurrentFrameURL() {
271   // No need to do anything if the WebContents is in the process of being
272   // destroyed anyway.
273   if (web_contents_->IsBeingDestroyed())
274     return;
275 
276   // If the interstitial was shown over an existing page, navigate back from
277   // that page. If that is not possible, attempt to close the entire tab.
278   if (web_contents_->GetController().CanGoBack()) {
279     web_contents_->GetController().GoBack();
280     return;
281   }
282 
283   TabCloser::MaybeClose(web_contents_);
284 }
285 
OnInterstitialDone()286 void SupervisedUserInterstitial::OnInterstitialDone() {
287   auto* navigation_observer =
288       SupervisedUserNavigationObserver::FromWebContents(web_contents_);
289 
290   // After this, the WebContents may be destroyed. Make sure we don't try to use
291   // it again.
292   web_contents_ = nullptr;
293   navigation_observer->OnInterstitialDone(frame_id_);
294 }
295