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