Chromium Code Reviews| OLD | NEW |
|---|---|
| (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 | |
| OLD | NEW |