chromium/ios/chrome/browser/supervised_user/model/supervised_user_url_filter_tab_helper.mm

// Copyright 2023 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/supervised_user/model/supervised_user_url_filter_tab_helper.h"

#import "base/functional/callback.h"
#import "base/memory/weak_ptr.h"
#import "base/strings/sys_string_conversions.h"
#import "components/supervised_user/core/browser/supervised_user_interstitial.h"
#import "components/supervised_user/core/browser/supervised_user_service.h"
#import "components/supervised_user/core/browser/supervised_user_url_filter.h"
#import "components/supervised_user/core/browser/supervised_user_utils.h"
#import "components/supervised_user/core/common/features.h"
#import "components/supervised_user/core/common/supervised_user_constants.h"
#import "ios/chrome/browser/shared/model/profile/profile_ios.h"
#import "ios/chrome/browser/supervised_user/model/supervised_user_capabilities.h"
#import "ios/chrome/browser/supervised_user/model/supervised_user_error.h"
#import "ios/chrome/browser/supervised_user/model/supervised_user_error_container.h"
#import "ios/chrome/browser/supervised_user/model/supervised_user_service_factory.h"
#import "ios/net/protocol_handler_util.h"
#import "net/base/apple/url_conversions.h"
#import "net/base/net_errors.h"
#import "url/gurl.h"

using PolicyDecision = web::WebStatePolicyDecider::PolicyDecision;

namespace {

void OnURLFilteringDone(
    base::WeakPtr<web::WebState> weak_web_state,
    GURL request_url,
    web::WebStatePolicyDecider::RequestInfo request_info,
    web::WebStatePolicyDecider::PolicyDecisionCallback policy_decision_callback,
    supervised_user::FilteringBehavior filtering_behavior,
    supervised_user::FilteringBehaviorReason reason,
    bool uncertain) {
  // Allow navigation by default.
  PolicyDecision decision = PolicyDecision::Allow();
  web::WebState* web_state = weak_web_state.get();

  if (!web_state) {
    // Cancel the request if the corresponding `web_state` is destroyed.
    decision = PolicyDecision::Cancel();
  } else if (filtering_behavior == supervised_user::FilteringBehavior::kBlock) {
    SupervisedUserErrorContainer* container =
        SupervisedUserErrorContainer::FromWebState(web_state);
    CHECK(container);
    container->SetSupervisedUserErrorInfo(
        std::make_unique<SupervisedUserErrorContainer::SupervisedUserErrorInfo>(
            request_url, request_info.target_frame_is_main, reason));
    decision = CreateSupervisedUserInterstitialErrorDecision();
  }

  supervised_user::SupervisedUserURLFilter::RecordFilterResultEvent(
      filtering_behavior, reason, /*is_filtering_behavior_known=*/!uncertain,
      request_info.transition_type);

  std::move(policy_decision_callback).Run(decision);
}

}  // namespace

SupervisedUserURLFilterTabHelper::~SupervisedUserURLFilterTabHelper() = default;

SupervisedUserURLFilterTabHelper::SupervisedUserURLFilterTabHelper(
    web::WebState* web_state)
    : web::WebStatePolicyDecider(web_state) {}

void SupervisedUserURLFilterTabHelper::ShouldAllowRequest(
    NSURLRequest* request,
    web::WebStatePolicyDecider::RequestInfo request_info,
    web::WebStatePolicyDecider::PolicyDecisionCallback callback) {
  ChromeBrowserState* chrome_browser_state =
      ChromeBrowserState::FromBrowserState(web_state()->GetBrowserState());

  // SupervisedUserService is not created for the off-the-record browser state.
  if (chrome_browser_state->IsOffTheRecord()) {
    std::move(callback).Run(PolicyDecision::Allow());
    return;
  }

  if (!supervised_user::IsSubjectToParentalControls(chrome_browser_state)) {
    std::move(callback).Run(PolicyDecision::Allow());
    return;
  }

  supervised_user::SupervisedUserService* supervised_user_service =
      SupervisedUserServiceFactory::GetForBrowserState(chrome_browser_state);

  // Set up the callback taking filtering results, and perform URL filtering.
  GURL request_url = net::GURLWithNSURL(request.URL);
  supervised_user::SupervisedUserURLFilter::FilteringBehaviorCallback
      filtering_behavior_callback =
          base::BindOnce(&OnURLFilteringDone, web_state()->GetWeakPtr(),
                         request_url, request_info, std::move(callback));

  supervised_user_service->GetURLFilter()
      ->GetFilteringBehaviorForURLWithAsyncChecks(
          request_url, std::move(filtering_behavior_callback),
          /*skip_manual_parent_filter=*/false);
}

WEB_STATE_USER_DATA_KEY_IMPL(SupervisedUserURLFilterTabHelper)