popup_tracker.cc 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. // Copyright 2017 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. #include "components/blocked_content/popup_tracker.h"
  5. #include <algorithm>
  6. #include "base/check.h"
  7. #include "base/memory/ptr_util.h"
  8. #include "base/metrics/histogram_macros.h"
  9. #include "base/time/default_tick_clock.h"
  10. #include "components/blocked_content/popup_opener_tab_helper.h"
  11. #include "content/public/browser/navigation_handle.h"
  12. #include "content/public/browser/web_contents.h"
  13. #include "services/metrics/public/cpp/metrics_utils.h"
  14. #include "services/metrics/public/cpp/ukm_builders.h"
  15. #include "services/metrics/public/cpp/ukm_recorder.h"
  16. namespace blocked_content {
  17. namespace {
  18. int CappedUserInteractions(int user_interactions, int max_interactions) {
  19. return std::min(user_interactions, max_interactions);
  20. }
  21. } // namespace
  22. PopupTracker* PopupTracker::CreateForWebContents(
  23. content::WebContents* contents,
  24. content::WebContents* opener,
  25. WindowOpenDisposition disposition) {
  26. DCHECK(contents);
  27. DCHECK(opener);
  28. auto* tracker = FromWebContents(contents);
  29. if (!tracker) {
  30. tracker = new PopupTracker(contents, opener, disposition);
  31. contents->SetUserData(UserDataKey(), base::WrapUnique(tracker));
  32. }
  33. return tracker;
  34. }
  35. PopupTracker::~PopupTracker() = default;
  36. PopupTracker::PopupTracker(content::WebContents* contents,
  37. content::WebContents* opener,
  38. WindowOpenDisposition disposition)
  39. : content::WebContentsObserver(contents),
  40. content::WebContentsUserData<PopupTracker>(*contents),
  41. visibility_tracker_(
  42. base::DefaultTickClock::GetInstance(),
  43. contents->GetVisibility() != content::Visibility::HIDDEN),
  44. opener_source_id_(opener->GetPrimaryMainFrame()->GetPageUkmSourceId()),
  45. window_open_disposition_(disposition) {
  46. if (auto* popup_opener = PopupOpenerTabHelper::FromWebContents(opener))
  47. popup_opener->OnOpenedPopup(this);
  48. auto* observation_manager =
  49. subresource_filter::SubresourceFilterObserverManager::FromWebContents(
  50. contents);
  51. if (observation_manager) {
  52. scoped_observation_.Observe(observation_manager);
  53. }
  54. }
  55. void PopupTracker::WebContentsDestroyed() {
  56. base::TimeDelta total_foreground_duration =
  57. visibility_tracker_.GetForegroundDuration();
  58. if (first_load_visible_time_start_) {
  59. base::TimeDelta first_load_visible_time =
  60. first_load_visible_time_
  61. ? *first_load_visible_time_
  62. : total_foreground_duration - *first_load_visible_time_start_;
  63. UMA_HISTOGRAM_LONG_TIMES(
  64. "ContentSettings.Popups.FirstDocumentEngagementTime2",
  65. first_load_visible_time);
  66. }
  67. UMA_HISTOGRAM_CUSTOM_TIMES("ContentSettings.Popups.EngagementTime",
  68. total_foreground_duration, base::Milliseconds(1),
  69. base::Hours(6), 50);
  70. if (web_contents()->GetClosedByUserGesture()) {
  71. UMA_HISTOGRAM_CUSTOM_TIMES(
  72. "ContentSettings.Popups.EngagementTime.GestureClose",
  73. total_foreground_duration, base::Milliseconds(1), base::Hours(6), 50);
  74. }
  75. if (opener_source_id_ != ukm::kInvalidSourceId) {
  76. const int kMaxInteractions = 100;
  77. const int kMaxSubcatagoryInteractions = 50;
  78. ukm::builders::Popup_Closed(opener_source_id_)
  79. .SetEngagementTime(ukm::GetExponentialBucketMinForUserTiming(
  80. total_foreground_duration.InMilliseconds()))
  81. .SetUserInitiatedClose(web_contents()->GetClosedByUserGesture())
  82. .SetTrusted(is_trusted_)
  83. .SetSafeBrowsingStatus(static_cast<int>(safe_browsing_status_))
  84. .SetWindowOpenDisposition(static_cast<int>(window_open_disposition_))
  85. .SetNumInteractions(
  86. CappedUserInteractions(num_interactions_, kMaxInteractions))
  87. .SetNumActivationInteractions(CappedUserInteractions(
  88. num_activation_events_, kMaxSubcatagoryInteractions))
  89. .SetNumGestureScrollBeginInteractions(CappedUserInteractions(
  90. num_gesture_scroll_begin_events_, kMaxSubcatagoryInteractions))
  91. .SetRedirectCount(num_redirects_)
  92. .Record(ukm::UkmRecorder::Get());
  93. }
  94. }
  95. void PopupTracker::DidFinishNavigation(
  96. content::NavigationHandle* navigation_handle) {
  97. if (!navigation_handle->HasCommitted() ||
  98. navigation_handle->IsSameDocument() ||
  99. !navigation_handle->IsInPrimaryMainFrame()) {
  100. return;
  101. }
  102. if (!first_navigation_committed_) {
  103. first_navigation_committed_ = true;
  104. // The last page in the redirect chain is the current page, the number of
  105. // redirects is one less than the size of the chain.
  106. num_redirects_ = navigation_handle->GetRedirectChain().size() - 1;
  107. }
  108. if (!first_load_visible_time_start_) {
  109. first_load_visible_time_start_ =
  110. visibility_tracker_.GetForegroundDuration();
  111. } else if (!first_load_visible_time_) {
  112. first_load_visible_time_ = visibility_tracker_.GetForegroundDuration() -
  113. *first_load_visible_time_start_;
  114. }
  115. }
  116. void PopupTracker::OnVisibilityChanged(content::Visibility visibility) {
  117. // TODO(csharrison): Consider handling OCCLUDED tabs the same way as HIDDEN
  118. // tabs.
  119. if (visibility == content::Visibility::HIDDEN)
  120. visibility_tracker_.OnHidden();
  121. else
  122. visibility_tracker_.OnShown();
  123. }
  124. void PopupTracker::DidGetUserInteraction(const blink::WebInputEvent& event) {
  125. // TODO(csharrison): It would be nice if ctrl-W could be filtered out here,
  126. // but the initial ctrl key press is registered as a kRawKeyDown.
  127. num_interactions_++;
  128. if (event.GetType() == blink::WebInputEvent::Type::kGestureScrollBegin) {
  129. num_gesture_scroll_begin_events_++;
  130. } else {
  131. num_activation_events_++;
  132. }
  133. }
  134. // This method will always be called before the DidFinishNavigation associated
  135. // with this handle.
  136. // The exception is a navigation restoring a page from back-forward cache --
  137. // in that case don't issue any requests, therefore we don't get any
  138. // safe browsing callbacks. See the comment above for the mitigation.
  139. void PopupTracker::OnSafeBrowsingChecksComplete(
  140. content::NavigationHandle* navigation_handle,
  141. const subresource_filter::SubresourceFilterSafeBrowsingClient::CheckResult&
  142. result) {
  143. DCHECK(navigation_handle->IsInMainFrame());
  144. if (!navigation_handle->IsInPrimaryMainFrame())
  145. return;
  146. safe_browsing_status_ = PopupSafeBrowsingStatus::kSafe;
  147. if (result.threat_type ==
  148. safe_browsing::SBThreatType::SB_THREAT_TYPE_URL_PHISHING ||
  149. result.threat_type == safe_browsing::SBThreatType::
  150. SB_THREAT_TYPE_URL_CLIENT_SIDE_PHISHING ||
  151. result.threat_type ==
  152. safe_browsing::SBThreatType::SB_THREAT_TYPE_SUBRESOURCE_FILTER) {
  153. safe_browsing_status_ = PopupSafeBrowsingStatus::kUnsafe;
  154. }
  155. }
  156. void PopupTracker::OnSubresourceFilterGoingAway() {
  157. DCHECK(scoped_observation_.IsObserving());
  158. scoped_observation_.Reset();
  159. }
  160. WEB_CONTENTS_USER_DATA_KEY_IMPL(PopupTracker);
  161. } // namespace blocked_content