Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(414)

Side by Side Diff: chrome/browser/safe_browsing/safe_browsing_navigation_observer.cc

Issue 2302913003: Add SafeBrowsingNavigationObserver to listen to navigation events (Closed)
Patch Set: nit Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 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/safe_browsing/safe_browsing_navigation_observer.h"
6
7 #include "base/memory/ptr_util.h"
8 #include "base/time/time.h"
9 #include "chrome/browser/chrome_notification_types.h"
10 #include "chrome/browser/sessions/session_tab_helper.h"
11 #include "chrome/browser/tab_contents/retargeting_details.h"
12 #include "content/public/browser/navigation_details.h"
13 #include "content/public/browser/navigation_handle.h"
14 #include "content/public/browser/notification_service.h"
15 #include "content/public/browser/notification_types.h"
16 #include "content/public/browser/render_frame_host.h"
17 #include "content/public/browser/render_process_host.h"
18 #include "content/public/browser/resource_request_details.h"
19 #include "content/public/browser/web_contents.h"
20 #include "content/public/common/resource_type.h"
21
22 using content::WebContents;
23 namespace safe_browsing {
24
25 static const double kUserGestureTTLInSecond = 0.5;
26
27 bool IsExpired(const base::Time& timestamp) {
Charlie Reis 2016/09/23 23:14:13 nit: IsUserGestureExpired?
Jialiu Lin 2016/09/27 17:50:59 Done.
28 double now = base::Time::Now().ToDoubleT();
29 double timestamp_in_double = timestamp.ToDoubleT();
30 return now > timestamp_in_double
31 ? now - timestamp_in_double > kUserGestureTTLInSecond
32 : true;
33 }
34
35 // SafeBrowsingNavigationObserver::NavigationEvent-----------------------------
36 NavigationEvent::NavigationEvent()
37 : source_url(),
38 source_tab_id(-1),
39 target_url(),
40 target_tab_id(-1),
41 frame_id(-1),
42 main_frame_url(),
43 timestamp(base::Time::Now()),
44 is_user_initiated(false),
45 has_committed(false),
46 is_server_redirect(false),
47 server_redirect_url() {}
48
49 NavigationEvent::NavigationEvent(NavigationEvent&& nav_event)
50 : source_url(std::move(nav_event.source_url)),
51 source_tab_id(std::move(nav_event.source_tab_id)),
52 target_url(std::move(nav_event.target_url)),
53 target_tab_id(std::move(nav_event.target_tab_id)),
54 frame_id(nav_event.frame_id),
55 main_frame_url(std::move(nav_event.main_frame_url)),
56 timestamp(nav_event.timestamp),
57 is_user_initiated(nav_event.is_user_initiated),
58 has_committed(nav_event.has_committed),
59 is_server_redirect(nav_event.is_server_redirect),
60 server_redirect_url(std::move(nav_event.server_redirect_url)) {}
61
62 NavigationEvent& NavigationEvent::operator=(NavigationEvent&& nav_event) {
63 source_url = std::move(nav_event.source_url);
64 source_tab_id = std::move(nav_event.source_tab_id);
65 target_url = std::move(nav_event.target_url);
66 target_tab_id = std::move(nav_event.target_tab_id);
67 frame_id = nav_event.frame_id;
68 main_frame_url = std::move(nav_event.main_frame_url);
69 timestamp = nav_event.timestamp;
70 is_user_initiated = nav_event.is_user_initiated;
71 has_committed = nav_event.has_committed;
72 is_server_redirect = nav_event.is_server_redirect;
73 server_redirect_url = std::move(nav_event.server_redirect_url);
74 return *this;
75 }
76
77 NavigationEvent::~NavigationEvent() {}
78
79 // -------------------SBNavigationObserverManager Functions----------------
80
81 SBNavigationObserverManager::SBNavigationObserverManager() {
82 registrar_.Add(this, chrome::NOTIFICATION_RETARGETING,
83 content::NotificationService::AllSources());
84 }
85
86 void SBNavigationObserverManager::RecordNavigationEvent(
87 const GURL& nav_event_key,
88 NavigationEvent* nav_event) {
89 auto insertion_result = navigation_map_.insert(
90 std::make_pair(nav_event_key, std::vector<NavigationEvent>()));
Charlie Reis 2016/09/23 23:14:14 Maybe I'm misreading this, but isn't this only nee
Jialiu Lin 2016/09/27 17:50:59 http://en.cppreference.com/w/cpp/container/unorder
Charlie Reis 2016/09/28 21:08:13 Acknowledged.
91
92 insertion_result.first->second.push_back(std::move(*nav_event));
93 }
94
95 void SBNavigationObserverManager::RecordUserGestureForWebContents(
96 content::WebContents* web_contents,
97 const base::Time& timestamp) {
98 auto insertion_result =
99 user_gesture_map_.insert(std::make_pair(web_contents, timestamp));
100 if (!insertion_result.second)
101 insertion_result.first->second = timestamp;
102 }
103
104 void SBNavigationObserverManager::OnUserGestureConsumed(
105 content::WebContents* web_contents,
106 const base::Time& timestamp) {
107 auto it = user_gesture_map_.find(web_contents);
108 if (it == user_gesture_map_.end())
109 return;
110 if (timestamp >= it->second)
111 user_gesture_map_.erase(web_contents);
112 }
113
114 void SBNavigationObserverManager::OnWebContentDestroyed(
115 content::WebContents* web_contents) {
116 user_gesture_map_.erase(web_contents);
117 // TODO (jialiul): Will add other clean up tasks shortly.
118 }
119
120 SBNavigationObserverManager::~SBNavigationObserverManager(){}
121
122 void SBNavigationObserverManager::Observe(
123 int type,
124 const content::NotificationSource& source,
125 const content::NotificationDetails& details) {
126 if (type == chrome::NOTIFICATION_RETARGETING)
127 RecordRetargeting(details);
128 }
129
130 void SBNavigationObserverManager::RecordRetargeting(
131 const content::NotificationDetails& details) {
132 const RetargetingDetails* retargeting_detail =
133 content::Details<const RetargetingDetails>(details).ptr();
134 DCHECK(retargeting_detail);
135 content::WebContents* source_contents =
136 retargeting_detail->source_web_contents;
137 content::WebContents* target_contents =
138 retargeting_detail->target_web_contents;
139 DCHECK(source_contents);
140 DCHECK(target_contents);
141
142 content::RenderFrameHost* rfh = content::RenderFrameHost::FromID(
143 source_contents->GetRenderProcessHost()->GetID(),
Charlie Reis 2016/09/23 23:14:13 This would break with OOPIFs, since WebContents::G
Jialiu Lin 2016/09/27 17:50:59 Thanks, I will add a comments here.
144 retargeting_detail->source_render_frame_id);
145 GURL::Replacements replacements;
146 replacements.ClearRef();
Charlie Reis 2016/09/23 23:14:13 Can you help me understand why we're clearing the
Jialiu Lin 2016/09/27 17:50:59 yes, this is to make "http://foo.com#bar" treated
Charlie Reis 2016/09/28 21:08:13 Sure. For both #bar and pushState/replaceState, w
Jialiu Lin 2016/09/28 21:39:14 I see. How about I just remove empty ref (a.k.a "
147 const GURL& target_url =
148 retargeting_detail->target_url.ReplaceComponents(replacements);
149
150 navigation_map_.insert(
151 std::make_pair(target_url, std::vector<NavigationEvent>()));
152
153 NavigationEvent nav_event;
154 nav_event.source_url =
155 rfh ? rfh->GetLastCommittedURL().ReplaceComponents(replacements) : GURL();
156 nav_event.source_tab_id = SessionTabHelper::IdForTab(source_contents);
157 nav_event.target_url = target_url;
158 nav_event.target_tab_id = SessionTabHelper::IdForTab(target_contents);
159 nav_event.frame_id = rfh ? rfh->GetFrameTreeNodeId() : -1;
160 nav_event.main_frame_url =
161 source_contents->GetLastCommittedURL().ReplaceComponents(replacements);
162 nav_event.timestamp = base::Time::Now();
163 auto it = user_gesture_map_.find(source_contents);
164 if (it != user_gesture_map_.end() && !IsExpired(it->second)) {
165 nav_event.is_user_initiated = true;
166 OnUserGestureConsumed(it->first, it->second);
167 } else {
168 nav_event.is_user_initiated = false;
169 }
170
171 navigation_map_[target_url].push_back(std::move(nav_event));
172 }
173
174 // -------------------SafeBrowsingNavigationObserver Functions----------------
175
176 // static
177 const char SafeBrowsingNavigationObserver::kWebContentsUserDataKey[] =
178 "web_contents_safe_browsing_navigation_observer";
179
180 // static
181 void SafeBrowsingNavigationObserver::MaybeCreateForWebContents(
182 content::WebContents* web_contents) {
183 if (FromWebContents(web_contents))
184 return;
185 // TODO(jialiul): This function will be called by TabHelpers::AttachTabHelpers
186 // Complete this function when the entire class is ready.
187 }
188
189 // static
190 SafeBrowsingNavigationObserver* SafeBrowsingNavigationObserver::FromWebContents(
191 content::WebContents* web_contents) {
192 return static_cast<SafeBrowsingNavigationObserver*>(
193 web_contents->GetUserData(kWebContentsUserDataKey));
194 }
195
196 SafeBrowsingNavigationObserver::SafeBrowsingNavigationObserver(
197 content::WebContents* contents,
198 const scoped_refptr<SBNavigationObserverManager>& manager)
199 : content::WebContentsObserver(contents),
200 manager_(manager),
201 has_user_gesture_(false),
202 last_user_gesture_timestamp_(base::Time::Now()) {}
203
204 SafeBrowsingNavigationObserver::~SafeBrowsingNavigationObserver() {}
205
206 // Called when a navigation started in the WebContents. |navigation_handle| in
207 // paramter is unique to this navigation, which will appear in the following
208 // DidRedirectNavigation, and DidFinishNavigation call too.
209 void SafeBrowsingNavigationObserver::DidStartNavigation(
210 content::NavigationHandle* navigation_handle) {
211 // If we already seen this navigation_handle before, no need to do anything.
212 if (navigation_handle_map_.find(navigation_handle) !=
213 navigation_handle_map_.end())
214 return;
215
216 // Construct a NavigationEvent based on available information in
217 // navigation_handle.
218 NavigationEvent nav_event;
219 content::RenderFrameHost* host =
220 navigation_handle->GetWebContents()->FindFrameByFrameTreeNodeId(
221 navigation_handle->GetFrameTreeNodeId());
Charlie Reis 2016/09/23 23:14:13 I'm concerned about this line, since the navigatio
Jialiu Lin 2016/09/27 17:50:59 Agree these are tricky cases. See my proposals bel
222
223 // If there was URL previously committed in this render frame host,
224 // set it as the source url of this navigation. Otherwise, this is the
225 // first url going to commit in this frame. We set navigation_handle's URL as
226 // the source url.
227 GURL::Replacements replacements;
228 replacements.ClearRef();
229 if (host && host->GetLastCommittedURL().is_valid()) {
230 nav_event.source_url =
231 host->GetLastCommittedURL().ReplaceComponents(replacements);
Charlie Reis 2016/09/23 23:14:13 This looks like it won't handle different frames o
Jialiu Lin 2016/09/27 17:50:59 There are two cases keep me awake at night. (1) t
Charlie Reis 2016/09/28 21:08:13 Note that my example could have put evil.com in if
Jialiu Lin 2016/09/28 21:39:14 You're right. it is quite indirect.
232 } else {
233 nav_event.source_url =
234 navigation_handle->GetURL().ReplaceComponents(replacements);
235 }
236
237 nav_event.target_url =
238 navigation_handle->GetURL().ReplaceComponents(replacements);
239
240 nav_event.source_tab_id =
241 SessionTabHelper::IdForTab(navigation_handle->GetWebContents());
242 nav_event.timestamp = base::Time::Now();
243 nav_event.frame_id = navigation_handle->GetFrameTreeNodeId();
244
245 if (navigation_handle->IsInMainFrame()) {
246 nav_event.main_frame_url = nav_event.source_url;
247 } else {
248 nav_event.main_frame_url = navigation_handle->GetWebContents()
249 ->GetLastCommittedURL()
250 .ReplaceComponents(replacements);
251 }
252 if (has_user_gesture_ && !IsExpired(last_user_gesture_timestamp_)) {
253 nav_event.is_user_initiated = has_user_gesture_;
254 manager_->OnUserGestureConsumed(web_contents(),
255 last_user_gesture_timestamp_);
256 }
257 has_user_gesture_ = false;
258 navigation_handle_map_.insert(
259 std::make_pair(navigation_handle, std::move(nav_event)));
260 }
261
262 void SafeBrowsingNavigationObserver::DidRedirectNavigation(
263 content::NavigationHandle* navigation_handle) {
264 // We should have already seen this navigation_handle in DidStartNavigation.
265 if (navigation_handle_map_.find(navigation_handle) ==
266 navigation_handle_map_.end())
267 return;
268
269 NavigationEvent* nav_event = &navigation_handle_map_[navigation_handle];
270 nav_event->is_server_redirect = true;
271 GURL::Replacements replacements;
272 replacements.ClearRef();
273 nav_event->server_redirect_url =
274 navigation_handle->GetURL().ReplaceComponents(replacements);
275 nav_event->timestamp = base::Time::Now();
276 }
277
278 void SafeBrowsingNavigationObserver::DidFinishNavigation(
279 content::NavigationHandle* navigation_handle) {
280 if (navigation_handle_map_.find(navigation_handle) ==
281 navigation_handle_map_.end())
282 return;
283
284 // If it is an error page, we ignore this navigation.
285 if (navigation_handle->IsErrorPage()) {
286 navigation_handle_map_.erase(navigation_handle);
287 return;
288 }
289 GURL::Replacements replacements;
290 replacements.ClearRef();
291 NavigationEvent* nav_event = &navigation_handle_map_[navigation_handle];
292
293 // Set is_user_initiated to has_user_gesture || browser_initiated.
294 nav_event->is_user_initiated =
295 nav_event->is_user_initiated || !navigation_handle->IsRendererInitiated();
296 nav_event->has_committed = navigation_handle->HasCommitted();
297 nav_event->target_tab_id =
298 SessionTabHelper::IdForTab(navigation_handle->GetWebContents());
299 nav_event->timestamp = base::Time::Now();
300
301 GURL nav_event_key = navigation_handle->HasCommitted()
302 ? navigation_handle->GetRenderFrameHost()
303 ->GetLastCommittedURL()
304 .ReplaceComponents(replacements)
305 : nav_event->is_server_redirect
306 ? nav_event->server_redirect_url
307 : nav_event->target_url;
308 manager_->RecordNavigationEvent(nav_event_key, nav_event);
309 navigation_handle_map_.erase(navigation_handle);
310 }
311
312 void SafeBrowsingNavigationObserver::DidGetUserInteraction(
313 const blink::WebInputEvent::Type type) {
314 last_user_gesture_timestamp_ = base::Time::Now();
315 has_user_gesture_ = true;
316 // TODO (jialiul): Refine user gesture logic when DidOpenRequestedURL
317 // covers all retargetting cases.
318 manager_->RecordUserGestureForWebContents(web_contents(),
319 last_user_gesture_timestamp_);
320 }
321
322 void SafeBrowsingNavigationObserver::WebContentsDestroyed() {
323 manager_->OnWebContentDestroyed(web_contents());
324 web_contents()->RemoveUserData(kWebContentsUserDataKey);
325 // web_contents is null after this function.
326 }
327
328 } // namespace safe_browsing
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698