blob: e18ae1a672ca43e2b7273e220fcdbb94ac938be3 [file] [log] [blame]
// 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 "chrome/browser/complex_tasks/task_tab_helper.h"
#include <string>
#include "base/logging.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "build/build_config.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/search_engines/template_url_service_factory.h"
#include "components/search_engines/template_url_service.h"
#include "content/public/browser/navigation_entry.h"
#if defined(OS_ANDROID)
#include "chrome/android/chrome_jni_headers/TaskTabHelper_jni.h"
#include "chrome/browser/android/tab_android.h"
using base::android::JavaParamRef;
#endif // defined(OS_ANDROID)
namespace {
bool DoesTransitionContinueTask(ui::PageTransition transition) {
return ui::PageTransitionCoreTypeIs(transition, ui::PAGE_TRANSITION_LINK) ||
ui::PageTransitionCoreTypeIs(transition,
ui::PAGE_TRANSITION_AUTO_SUBFRAME) ||
ui::PageTransitionCoreTypeIs(transition,
ui::PAGE_TRANSITION_MANUAL_SUBFRAME) ||
ui::PageTransitionCoreTypeIs(transition,
ui::PAGE_TRANSITION_FORM_SUBMIT) ||
transition & ui::PAGE_TRANSITION_IS_REDIRECT_MASK;
}
} // namespace
namespace tasks {
TaskTabHelper::TaskTabHelper(content::WebContents* web_contents)
: content::WebContentsObserver(web_contents),
last_pruned_navigation_entry_index_(-1) {}
TaskTabHelper::~TaskTabHelper() {}
TaskTabHelper::HubType TaskTabHelper::GetSpokeEntryHubType() const {
content::NavigationEntry* entry =
web_contents()->GetController().GetLastCommittedEntry();
DCHECK(entry);
Profile* profile =
Profile::FromBrowserContext(web_contents()->GetBrowserContext());
TemplateURLService* url_service =
TemplateURLServiceFactory::GetForProfile(profile);
if (url_service && url_service->IsSearchResultsPageFromDefaultSearchProvider(
entry->GetURL())) {
return HubType::DEFAULT_SEARCH_ENGINE;
} else if (ui::PageTransitionCoreTypeIs(
entry->GetTransitionType(),
ui::PageTransition::PAGE_TRANSITION_FORM_SUBMIT)) {
return HubType::FORM_SUBMIT;
} else {
return HubType::OTHER;
}
}
void TaskTabHelper::NavigationEntryCommitted(
const content::LoadCommittedDetails& load_details) {
int current_entry_index =
web_contents()->GetController().GetCurrentEntryIndex();
if (current_entry_index > last_pruned_navigation_entry_index_)
entry_index_to_spoke_count_map_[current_entry_index] = 1;
UpdateAndRecordTaskIds(load_details);
}
void TaskTabHelper::NavigationListPruned(
const content::PrunedDetails& pruned_details) {
int current_entry_index =
web_contents()->GetController().GetCurrentEntryIndex();
if (entry_index_to_spoke_count_map_.count(current_entry_index) == 0)
entry_index_to_spoke_count_map_[current_entry_index] = 1;
entry_index_to_spoke_count_map_[current_entry_index]++;
last_pruned_navigation_entry_index_ = current_entry_index;
RecordHubAndSpokeNavigationUsage(
entry_index_to_spoke_count_map_[current_entry_index]);
}
sessions::ContextRecordTaskId* TaskTabHelper::GetContextRecordTaskId(
content::WebContents* web_contents) {
if (!web_contents)
return nullptr;
content::NavigationEntry* navigation_entry =
web_contents->GetController().GetLastCommittedEntry();
if (!navigation_entry)
return nullptr;
sessions::ContextRecordTaskId* context_record_task_id =
sessions::ContextRecordTaskId::Get(navigation_entry);
if (!context_record_task_id)
return nullptr;
return context_record_task_id;
}
void TaskTabHelper::UpdateAndRecordTaskIds(
const content::LoadCommittedDetails& load_details) {
sessions::ContextRecordTaskId* context_record_task_id =
sessions::ContextRecordTaskId::Get(load_details.entry);
// The Task ID is the Global ID of the first navigation. The first
// navigation is detected if the Task ID hasn't been set yet.
if (context_record_task_id->task_id() == -1) {
context_record_task_id->set_task_id(
load_details.entry->GetTimestamp().since_origin().InMicroseconds());
}
if (DoesTransitionContinueTask(load_details.entry->GetTransitionType())) {
if (load_details.previous_entry_index != -1) {
content::NavigationEntry* prev_nav_entry =
web_contents()->GetController().GetEntryAtIndex(
load_details.previous_entry_index);
if (prev_nav_entry != nullptr) {
sessions::ContextRecordTaskId* prev_context_record_task_id =
sessions::ContextRecordTaskId::Get(prev_nav_entry);
context_record_task_id->set_parent_task_id(
prev_context_record_task_id->task_id());
context_record_task_id->set_root_task_id(
prev_context_record_task_id->root_task_id());
}
#if defined(OS_ANDROID)
else {
// Cross-tab navigation - only supported in Android. In this case
// the parent and parent root Task IDs are passed from the Java layer
if (this->GetParentTaskId() != -1) {
context_record_task_id->set_parent_task_id(this->GetParentTaskId());
context_record_task_id->set_root_task_id(this->GetParentRootTaskId());
}
}
#endif // defined(OS_ANDROID)
}
} else {
context_record_task_id->set_root_task_id(context_record_task_id->task_id());
}
local_context_record_task_id_map_.emplace(load_details.entry->GetUniqueID(),
*context_record_task_id);
}
void TaskTabHelper::RecordHubAndSpokeNavigationUsage(int spokes) {
DCHECK_GT(spokes, 1);
std::string histogram_name;
switch (GetSpokeEntryHubType()) {
case HubType::DEFAULT_SEARCH_ENGINE: {
histogram_name =
"Tabs.Tasks.HubAndSpokeNavigationUsage.FromDefaultSearchEngine";
break;
}
case HubType::FORM_SUBMIT: {
histogram_name = "Tabs.Tasks.HubAndSpokeNavigationUsage.FromFormSubmit";
break;
}
case HubType::OTHER: {
histogram_name = "Tabs.Tasks.HubAndSpokeNavigationUsage.FromOther";
break;
}
default: {
NOTREACHED() << "Unknown HubType";
}
}
base::UmaHistogramExactLinear(histogram_name, spokes, 100);
}
#if defined(OS_ANDROID)
int64_t TaskTabHelper::GetParentTaskId() {
TabAndroid* tab_android = TabAndroid::FromWebContents(web_contents());
return Java_TaskTabHelper_getParentTaskId(
base::android::AttachCurrentThread(), tab_android->GetJavaObject());
}
int64_t TaskTabHelper::GetParentRootTaskId() {
TabAndroid* tab_android = TabAndroid::FromWebContents(web_contents());
return Java_TaskTabHelper_getParentRootTaskId(
base::android::AttachCurrentThread(), tab_android->GetJavaObject());
}
jlong JNI_TaskTabHelper_GetTaskId(JNIEnv* env,
const JavaParamRef<jobject>& jweb_contents) {
sessions::ContextRecordTaskId* context_record_task_id =
TaskTabHelper::GetContextRecordTaskId(
content::WebContents::FromJavaWebContents(jweb_contents));
if (context_record_task_id) {
return context_record_task_id->task_id();
}
return -1;
}
jlong JNI_TaskTabHelper_GetRootTaskId(
JNIEnv* env,
const JavaParamRef<jobject>& jweb_contents) {
sessions::ContextRecordTaskId* context_record_task_id =
TaskTabHelper::GetContextRecordTaskId(
content::WebContents::FromJavaWebContents(jweb_contents));
if (context_record_task_id) {
return context_record_task_id->root_task_id();
}
return -1;
}
#endif // defined(OS_ANDROID)
WEB_CONTENTS_USER_DATA_KEY_IMPL(TaskTabHelper)
} // namespace tasks