| // Copyright 2019 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/optimization_guide/optimization_guide_web_contents_observer.h" |
| |
| #include "chrome/browser/optimization_guide/optimization_guide_keyed_service.h" |
| #include "chrome/browser/optimization_guide/optimization_guide_keyed_service_factory.h" |
| #include "chrome/browser/optimization_guide/optimization_guide_top_host_provider.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "components/optimization_guide/hints_fetcher.h" |
| #include "components/optimization_guide/hints_processing_util.h" |
| #include "components/optimization_guide/optimization_guide_enums.h" |
| #include "components/optimization_guide/optimization_guide_features.h" |
| #include "components/optimization_guide/proto/hints.pb.h" |
| #include "components/page_load_metrics/common/page_load_metrics.mojom.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/navigation_handle.h" |
| |
| namespace { |
| |
| bool IsValidOptimizationGuideNavigation( |
| content::NavigationHandle* navigation_handle) { |
| return navigation_handle->IsInMainFrame() && |
| navigation_handle->GetURL().SchemeIsHTTPOrHTTPS(); |
| } |
| |
| } // namespace |
| |
| OptimizationGuideWebContentsObserver::OptimizationGuideWebContentsObserver( |
| content::WebContents* web_contents) |
| : content::WebContentsObserver(web_contents) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| |
| optimization_guide_keyed_service_ = |
| OptimizationGuideKeyedServiceFactory::GetForProfile( |
| Profile::FromBrowserContext(web_contents->GetBrowserContext())); |
| } |
| |
| OptimizationGuideWebContentsObserver::~OptimizationGuideWebContentsObserver() = |
| default; |
| |
| OptimizationGuideNavigationData* OptimizationGuideWebContentsObserver:: |
| GetOrCreateOptimizationGuideNavigationData( |
| content::NavigationHandle* navigation_handle) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| |
| int64_t navigation_id = navigation_handle->GetNavigationId(); |
| if (inflight_optimization_guide_navigation_datas_.find(navigation_id) == |
| inflight_optimization_guide_navigation_datas_.end()) { |
| // We do not have one already - create one. |
| inflight_optimization_guide_navigation_datas_[navigation_id] = |
| std::make_unique<OptimizationGuideNavigationData>(navigation_id); |
| } |
| |
| DCHECK(inflight_optimization_guide_navigation_datas_.find(navigation_id) != |
| inflight_optimization_guide_navigation_datas_.end()); |
| return inflight_optimization_guide_navigation_datas_.find(navigation_id) |
| ->second.get(); |
| } |
| |
| void OptimizationGuideWebContentsObserver::DidStartNavigation( |
| content::NavigationHandle* navigation_handle) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| if (!IsValidOptimizationGuideNavigation(navigation_handle)) |
| return; |
| |
| content::WebContents* web_contents = navigation_handle->GetWebContents(); |
| bool is_same_origin = |
| web_contents && |
| web_contents->GetLastCommittedURL().SchemeIsHTTPOrHTTPS() && |
| url::IsSameOriginWith(navigation_handle->GetURL(), |
| web_contents->GetLastCommittedURL()); |
| OptimizationGuideTopHostProvider::MaybeUpdateTopHostBlacklist( |
| navigation_handle); |
| |
| if (!optimization_guide_keyed_service_) |
| return; |
| |
| optimization_guide_keyed_service_->OnNavigationStartOrRedirect( |
| navigation_handle); |
| OptimizationGuideNavigationData* nav_data = |
| GetOrCreateOptimizationGuideNavigationData(navigation_handle); |
| nav_data->set_is_same_origin_navigation(is_same_origin); |
| } |
| |
| void OptimizationGuideWebContentsObserver::DidRedirectNavigation( |
| content::NavigationHandle* navigation_handle) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| |
| if (!IsValidOptimizationGuideNavigation(navigation_handle)) |
| return; |
| |
| if (!optimization_guide_keyed_service_) |
| return; |
| |
| optimization_guide_keyed_service_->OnNavigationStartOrRedirect( |
| navigation_handle); |
| } |
| |
| void OptimizationGuideWebContentsObserver::DidFinishNavigation( |
| content::NavigationHandle* navigation_handle) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| |
| if (!IsValidOptimizationGuideNavigation(navigation_handle)) |
| return; |
| |
| // Delete Optimization Guide information later, so that other |
| // DidFinishNavigation methods can reliably use |
| // GetOptimizationGuideNavigationData regardless of order of |
| // WebContentsObservers. |
| // Note that a lot of Navigations (sub-frames, same document, non-committed, |
| // etc.) might not have navigation data associated with them, but we reduce |
| // likelihood of future leaks by always trying to remove the data. |
| base::ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, |
| base::BindOnce( |
| &OptimizationGuideWebContentsObserver::NotifyNavigationFinish, |
| weak_factory_.GetWeakPtr(), navigation_handle->GetNavigationId(), |
| navigation_handle->GetRedirectChain())); |
| } |
| |
| void OptimizationGuideWebContentsObserver::NotifyNavigationFinish( |
| int64_t navigation_id, |
| const std::vector<GURL>& navigation_redirect_chain) { |
| auto nav_data_iter = |
| inflight_optimization_guide_navigation_datas_.find(navigation_id); |
| if (nav_data_iter == inflight_optimization_guide_navigation_datas_.end()) |
| return; |
| |
| if (optimization_guide_keyed_service_) { |
| optimization_guide_keyed_service_->OnNavigationFinish( |
| navigation_redirect_chain); |
| } |
| |
| // We keep the last navigation data around to keep track of events happening |
| // for the navigation that can happen after commit, such as a fetch for the |
| // navigation successfully completing (which is not guaranteed to come back |
| // before commit, if at all). |
| last_navigation_data_ = std::move(nav_data_iter->second); |
| inflight_optimization_guide_navigation_datas_.erase(navigation_id); |
| } |
| |
| void OptimizationGuideWebContentsObserver::UpdateSessionTimingStatistics( |
| const page_load_metrics::mojom::PageLoadTiming& timing) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| if (!optimization_guide_keyed_service_) |
| return; |
| |
| optimization_guide_keyed_service_->UpdateSessionFCP( |
| timing.paint_timing->first_contentful_paint.value()); |
| } |
| |
| void OptimizationGuideWebContentsObserver::FlushLastNavigationData() { |
| if (last_navigation_data_) |
| last_navigation_data_.reset(); |
| } |
| |
| WEB_CONTENTS_USER_DATA_KEY_IMPL(OptimizationGuideWebContentsObserver) |