chromium/ios/chrome/browser/complex_tasks/model/ios_task_tab_helper.mm

// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#import "ios/chrome/browser/complex_tasks/model/ios_task_tab_helper.h"

#import <string>

#import "ios/web/public/navigation/navigation_context.h"
#import "ios/web/public/navigation/navigation_item.h"
#import "ios/web/public/navigation/navigation_manager.h"

namespace {
// Determines if transition means the navigation will be in the same task
// or start a new task. For example, clicking on a link continues the
// current task. Starting a new search in the omnibox starts a new task.
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

IOSTaskTabHelper::IOSTaskTabHelper(web::WebState* web_state)
    : web_state_(web_state) {
  web_state_->AddObserver(this);
}

IOSTaskTabHelper::~IOSTaskTabHelper() {
  if (web_state_) {
    web_state_->RemoveObserver(this);
    web_state_ = nullptr;
  }
}

void IOSTaskTabHelper::DidStartNavigation(
    web::WebState* web_state,
    web::NavigationContext* navigation_context) {
  web::NavigationManager* navigation_manager =
      web_state->GetNavigationManager();
  if (navigation_manager->GetLastCommittedItem()) {
    prev_item_unique_id_ =
        navigation_manager->GetLastCommittedItem()->GetUniqueID();
  }
}

void IOSTaskTabHelper::DidFinishNavigation(
    web::WebState* web_state,
    web::NavigationContext* navigation_context) {
  web::NavigationManager* navigation_manager =
      web_state->GetNavigationManager();
  web::NavigationItem* last_committed_item =
      navigation_manager->GetLastCommittedItem();
  if (!last_committed_item) {
    return;
  }
  IOSContentRecordTaskId ios_content_record_task_id;
  ios_content_record_task_id.set_task_id(
      last_committed_item->GetTimestamp().since_origin().InMicroseconds());

  if (DoesTransitionContinueTask(navigation_context->GetPageTransition())) {
    if (prev_item_unique_id_ != -1) {
      IOSContentRecordTaskId prev =
          ios_content_record_task_id_map_[prev_item_unique_id_];
      ios_content_record_task_id.set_parent_task_id(prev.task_id());
      ios_content_record_task_id.set_root_task_id(prev.root_task_id());
    } else {
      ios_content_record_task_id.set_root_task_id(
          ios_content_record_task_id.task_id());
    }
  } else {
    ios_content_record_task_id.set_root_task_id(
        ios_content_record_task_id.task_id());
  }
  ios_content_record_task_id_map_.emplace(last_committed_item->GetUniqueID(),
                                          ios_content_record_task_id);
  prev_item_unique_id_ = -1;
}

void IOSTaskTabHelper::WebStateDestroyed(web::WebState* web_state) {
  web_state->RemoveObserver(this);
  web_state_ = nullptr;
}

const IOSContentRecordTaskId* IOSTaskTabHelper::GetContextRecordTaskId(
    int nav_id) const {
  auto result = ios_content_record_task_id_map_.find(nav_id);
  return result != ios_content_record_task_id_map_.end() ? &result->second
                                                         : nullptr;
}

WEB_STATE_USER_DATA_KEY_IMPL(IOSTaskTabHelper)