123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171 |
- // Copyright 2018 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 "components/ukm/ios/ukm_url_recorder.h"
- #include <utility>
- #include "base/containers/flat_map.h"
- #import "ios/web/public/navigation/navigation_context.h"
- #import "ios/web/public/web_state.h"
- #include "ios/web/public/web_state_observer.h"
- #import "ios/web/public/web_state_user_data.h"
- #include "services/metrics/public/cpp/delegating_ukm_recorder.h"
- #include "services/metrics/public/cpp/ukm_source_id.h"
- #include "url/gurl.h"
- #if !defined(__has_feature) || !__has_feature(objc_arc)
- #error "This file requires ARC support."
- #endif
- namespace ukm {
- namespace internal {
- // SourceUrlRecorderWebStateObserver is responsible for recording UKM source
- // URLs for all main frame navigations in a given WebState.
- // SourceUrlRecorderWebStateObserver records both the final URL for a
- // navigation and, if the navigation was redirected, the initial URL as well.
- class SourceUrlRecorderWebStateObserver
- : public web::WebStateObserver,
- public web::WebStateUserData<SourceUrlRecorderWebStateObserver> {
- public:
- SourceUrlRecorderWebStateObserver(const SourceUrlRecorderWebStateObserver&) =
- delete;
- SourceUrlRecorderWebStateObserver& operator=(
- const SourceUrlRecorderWebStateObserver&) = delete;
- ~SourceUrlRecorderWebStateObserver() override;
- // web::WebStateObserver
- void DidStartNavigation(web::WebState* web_state,
- web::NavigationContext* navigation_context) override;
- void DidFinishNavigation(web::WebState* web_state,
- web::NavigationContext* navigation_context) override;
- void WebStateDestroyed(web::WebState* web_state) override;
- SourceId GetLastCommittedSourceId() const;
- private:
- explicit SourceUrlRecorderWebStateObserver(web::WebState* web_state);
- friend class web::WebStateUserData<SourceUrlRecorderWebStateObserver>;
- void MaybeRecordUrl(web::NavigationContext* navigation_context,
- const GURL& initial_url);
- // Map from navigation ID to the initial URL for that navigation.
- base::flat_map<int64_t, GURL> pending_navigations_;
- SourceId last_committed_source_id_;
- WEB_STATE_USER_DATA_KEY_DECL();
- };
- WEB_STATE_USER_DATA_KEY_IMPL(SourceUrlRecorderWebStateObserver)
- SourceUrlRecorderWebStateObserver::SourceUrlRecorderWebStateObserver(
- web::WebState* web_state)
- : last_committed_source_id_(kInvalidSourceId) {
- web_state->AddObserver(this);
- }
- SourceUrlRecorderWebStateObserver::~SourceUrlRecorderWebStateObserver() {}
- void SourceUrlRecorderWebStateObserver::WebStateDestroyed(
- web::WebState* web_state) {
- web_state->RemoveObserver(this);
- }
- void SourceUrlRecorderWebStateObserver::DidStartNavigation(
- web::WebState* web_state,
- web::NavigationContext* navigation_context) {
- // NavigationContexts only exist for main frame navigations, so this will
- // never be called for non-main frame navigations and we don't need to filter
- // non-main frame navigations out here.
- // Additionally, at least for the time being, we don't track metrics for
- // same-document navigations (e.g. changes in URL fragment or URL changes
- // due to history.pushState) in UKM.
- if (navigation_context->IsSameDocument())
- return;
- // UKM doesn't want to record URLs for downloads. However, at the point a
- // navigation is started, we don't yet know if the navigation will result in a
- // download. Thus, we store the URL at the time a navigation was initiated
- // and only record it later, once we verify that the navigation didn't result
- // in a download.
- pending_navigations_.insert(std::make_pair(
- navigation_context->GetNavigationId(), navigation_context->GetUrl()));
- }
- void SourceUrlRecorderWebStateObserver::DidFinishNavigation(
- web::WebState* web_state,
- web::NavigationContext* navigation_context) {
- // NavigationContexts only exist for main frame navigations, so this will
- // never be called for non-main frame navigations and we don't need to filter
- // non-main frame navigations out here.
- auto it = pending_navigations_.find(navigation_context->GetNavigationId());
- if (it == pending_navigations_.end())
- return;
- DCHECK(!navigation_context->IsSameDocument());
- if (navigation_context->HasCommitted()) {
- last_committed_source_id_ = ConvertToSourceId(
- navigation_context->GetNavigationId(), SourceIdType::NAVIGATION_ID);
- }
- GURL initial_url = std::move(it->second);
- pending_navigations_.erase(it);
- // UKM doesn't want to record URLs for navigations that result in downloads.
- if (navigation_context->IsDownload())
- return;
- MaybeRecordUrl(navigation_context, initial_url);
- }
- SourceId SourceUrlRecorderWebStateObserver::GetLastCommittedSourceId() const {
- return last_committed_source_id_;
- }
- void SourceUrlRecorderWebStateObserver::MaybeRecordUrl(
- web::NavigationContext* navigation_context,
- const GURL& initial_url) {
- DCHECK(!navigation_context->IsSameDocument());
- DelegatingUkmRecorder* ukm_recorder = DelegatingUkmRecorder::Get();
- if (!ukm_recorder)
- return;
- const GURL& final_url = navigation_context->GetUrl();
- UkmSource::NavigationData navigation_data;
- // TODO(crbug.com/869123): This check isn't quite correct, as self redirecting
- // is possible. This may also be changed to include the entire redirect chain.
- if (final_url != initial_url)
- navigation_data.urls = {initial_url};
- navigation_data.urls.push_back(final_url);
- // TODO(crbug.com/873316): Fill out the other fields in NavigationData.
- const ukm::SourceId source_id = ukm::ConvertToSourceId(
- navigation_context->GetNavigationId(), ukm::SourceIdType::NAVIGATION_ID);
- ukm_recorder->RecordNavigation(source_id, navigation_data);
- }
- } // namespace internal
- void InitializeSourceUrlRecorderForWebState(web::WebState* web_state) {
- internal::SourceUrlRecorderWebStateObserver::CreateForWebState(web_state);
- }
- SourceId GetSourceIdForWebStateDocument(web::WebState* web_state) {
- internal::SourceUrlRecorderWebStateObserver* obs =
- internal::SourceUrlRecorderWebStateObserver::FromWebState(web_state);
- return obs ? obs->GetLastCommittedSourceId() : kInvalidSourceId;
- }
- } // namespace ukm
|