| // Copyright 2017 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/ui/blocked_content/popup_opener_tab_helper.h" |
| |
| #include "base/memory/ptr_util.h" |
| #include "base/metrics/histogram_macros.h" |
| #include "base/time/default_tick_clock.h" |
| #include "chrome/browser/ui/blocked_content/scoped_visibility_tracker.h" |
| #include "content/public/browser/navigation_handle.h" |
| #include "content/public/browser/web_contents.h" |
| #include "url/gurl.h" |
| #include "url/origin.h" |
| |
| DEFINE_WEB_CONTENTS_USER_DATA_KEY(PopupOpenerTabHelper); |
| |
| PopupOpenerTabHelper::~PopupOpenerTabHelper() { |
| if (!visibility_tracker_) |
| return; |
| base::TimeDelta foreground_duration = |
| visibility_tracker_->GetForegroundDuration(); |
| UMA_HISTOGRAM_LONG_TIMES("Tab.VisibleTimeAfterCrossOriginRedirect", |
| foreground_duration); |
| if (!last_popup_open_time_before_redirect_.is_null()) { |
| UMA_HISTOGRAM_LONG_TIMES( |
| "Tab.OpenedPopup.VisibleTimeAfterCrossOriginRedirect", |
| foreground_duration); |
| } |
| } |
| |
| void PopupOpenerTabHelper::OnOpenedPopup(PopupTracker* popup_tracker) { |
| if (!visibility_tracker_) { |
| // Should still have clock ownership, since it is passed to the visibility |
| // tracker when that is instantiated. |
| DCHECK(tick_clock_); |
| last_popup_open_time_before_redirect_ = tick_clock_->NowTicks(); |
| } |
| } |
| |
| PopupOpenerTabHelper::PopupOpenerTabHelper(content::WebContents* web_contents) |
| : content::WebContentsObserver(web_contents), |
| tick_clock_(base::MakeUnique<base::DefaultTickClock>()) {} |
| |
| void PopupOpenerTabHelper::DidStartNavigation( |
| content::NavigationHandle* navigation_handle) { |
| if (visibility_tracker_) |
| return; |
| |
| if (navigation_handle->IsInMainFrame() && !web_contents()->IsVisible()) |
| pending_background_navigations_.insert(navigation_handle); |
| // There should be at max 2 main frame navigations occurring at the same time. |
| DCHECK_LE(pending_background_navigations_.size(), 2u); |
| } |
| |
| void PopupOpenerTabHelper::DidFinishNavigation( |
| content::NavigationHandle* navigation_handle) { |
| if (visibility_tracker_ || !navigation_handle->IsInMainFrame()) |
| return; |
| |
| size_t num_erased = pending_background_navigations_.erase(navigation_handle); |
| if (!num_erased || !navigation_handle->HasCommitted() || |
| navigation_handle->IsErrorPage()) { |
| return; |
| } |
| |
| // Only consider renderer-initiated navigations without a user gesture. |
| if (navigation_handle->HasUserGesture() || |
| !navigation_handle->IsRendererInitiated()) { |
| return; |
| } |
| |
| // An empty previous URL indicates this was the first load. We filter these |
| // out because we're primarily interested in sites which navigate themselves |
| // away while in the background. |
| const GURL& previous_main_frame_url = navigation_handle->GetPreviousURL(); |
| if (previous_main_frame_url.is_empty()) |
| return; |
| |
| // Only track cross-origin navigations. |
| if (url::Origin(previous_main_frame_url) |
| .IsSameOriginWith(url::Origin(navigation_handle->GetURL()))) { |
| return; |
| } |
| |
| // Use the tick clock before passing ownership. |
| if (!last_popup_open_time_before_redirect_.is_null()) { |
| // If long times doesn't have enough resolution, consider switching the |
| // macro. |
| UMA_HISTOGRAM_LONG_TIMES( |
| "Tab.OpenedPopup.PopupToCrossOriginRedirectTime", |
| tick_clock_->NowTicks() - last_popup_open_time_before_redirect_); |
| } |
| |
| visibility_tracker_ = base::MakeUnique<ScopedVisibilityTracker>( |
| std::move(tick_clock_), false /* is_visible */); |
| pending_background_navigations_.clear(); |
| } |
| |
| void PopupOpenerTabHelper::WasShown() { |
| if (visibility_tracker_) |
| visibility_tracker_->OnShown(); |
| } |
| |
| void PopupOpenerTabHelper::WasHidden() { |
| if (visibility_tracker_) |
| visibility_tracker_->OnHidden(); |
| } |