text: // Copyright 2015 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.
  
  #ifndef IOS_WEB_PUBLIC_NAVIGATION_WEB_STATE_POLICY_DECIDER_H_
  #define IOS_WEB_PUBLIC_NAVIGATION_WEB_STATE_POLICY_DECIDER_H_
  
  #import <Foundation/Foundation.h>
  
  #include "base/callback.h"
  #include "base/macros.h"
  #include "ui/base/page_transition_types.h"
  #include "url/gurl.h"
  
  namespace web {
  
  class WebState;
  class FakeWebState;
  
  // Decides the navigation policy for a web state.
  class WebStatePolicyDecider {
   public:
    // Specifies a navigation decision. Used as a return value by
    // WebStatePolicyDecider::ShouldAllowRequest(), and used by
    // WebStatePolicyDecider::ShouldAllowResponse() when sending its decision to
    // its callback.
    struct PolicyDecision {
      // A policy decision which allows the navigation.
      static PolicyDecision Allow();
  
      // A policy decision which cancels the navigation.
      static PolicyDecision Cancel();
  
      // A policy decision which cancels the navigation and displays |error|.
      // NOTE: The |error| will only be displayed if the associated navigation is
      // being loaded in the main frame.
      static PolicyDecision CancelAndDisplayError(NSError* error);
  
      // Whether or not the navigation will continue.
      bool ShouldAllowNavigation() const;
  
      // Whether or not the navigation will be cancelled.
      bool ShouldCancelNavigation() const;
  
      // Whether or not an error should be displayed. Always returns false if
      // |ShouldAllowNavigation| is true.
      // NOTE: Will return true when the receiver is created with
      // |CancelAndDisplayError| even though an error will only end up being
      // displayed if the associated navigation is occurring in the main frame.
      bool ShouldDisplayError() const;
  
      // The error to display when |ShouldDisplayError| is true.
      NSError* GetDisplayError() const;
  
     private:
      // The decisions which can be taken for a given navigation.
      enum class Decision {
        // Allow the navigation to proceed.
        kAllow,
  
        // Cancel the navigation.
        kCancel,
  
        // Cancel the navigation and display an error.
        kCancelAndDisplayError,
      };
  
      PolicyDecision(Decision decision, NSError* error)
          : decision(decision), error(error) {}
  
      // The decision to be taken for a given navigation.
      Decision decision = Decision::kAllow;
  
      // An error associated with the navigation. This error will be displayed if
      // |decision| is |kCancelAndDisplayError|.
      NSError* error = nil;
    };
  
    // Callback used to provide asynchronous policy decisions.
    typedef base::OnceCallback<void(PolicyDecision)> PolicyDecisionCallback;
  
    // Data Transfer Object for the additional information about navigation
    // request passed to WebStatePolicyDecider::ShouldAllowRequest().
    struct RequestInfo {
      RequestInfo(ui::PageTransition transition_type,
                  bool target_frame_is_main,
                  bool target_frame_is_cross_origin,
                  bool has_user_gesture)
          : transition_type(transition_type),
            target_frame_is_main(target_frame_is_main),
            target_frame_is_cross_origin(target_frame_is_cross_origin),
            has_user_gesture(has_user_gesture) {}
      // The navigation page transition type.
      ui::PageTransition transition_type =
          ui::PageTransition::PAGE_TRANSITION_FIRST;
      // Indicates whether the navigation target frame is the main frame.
      bool target_frame_is_main = false;
      // Indicates whether the navigation target frame is cross-origin with
      // respect to the the navigation source frame.
      bool target_frame_is_cross_origin = false;
      // Indicates if there was a recent user interaction with the request frame.
      bool has_user_gesture = false;
    };
  
    // Data Transfer Object for the additional information about response
    // request passed to WebStatePolicyDecider::ShouldAllowResponse().
    struct ResponseInfo {
      explicit ResponseInfo(bool for_main_frame)
          : for_main_frame(for_main_frame) {}
      // Indicates whether the response target frame is the main frame.
      bool for_main_frame = false;
    };
  
    WebStatePolicyDecider(const WebStatePolicyDecider&) = delete;
    WebStatePolicyDecider& operator=(const WebStatePolicyDecider&) = delete;
  
    // Removes self as a policy decider of |web_state_|.
    virtual ~WebStatePolicyDecider();
  
    // Asks the decider whether the navigation corresponding to |request| should
    // be allowed to continue. Defaults to PolicyDecision::Allow() if not
    // overridden. Called before WebStateObserver::DidStartNavigation. Calls
    // |callback| with the decision. Never called in the following cases:
    //  - same-document back-forward and state change navigations
    virtual void ShouldAllowRequest(NSURLRequest* request,
                                    RequestInfo request_info,
                                    PolicyDecisionCallback callback);
  
    // Asks the decider whether the navigation corresponding to |response| should
    // be allowed to display an error page if an error occurs. Defaults to
    // true if not overridden. This can be used to suppress error pages in certain
    // cases such as attempting to upgrade an omnibox navigation to HTTPS. In that
    // scenario, failed upgrade attempts (e.g. due to SSL or DNS resolution
    // errors) should immediately fall back to HTTP without showing an error page.
    virtual bool ShouldAllowErrorPageToBeDisplayed(NSURLResponse* response,
                                                   bool for_main_frame);
  
    // Asks the decider whether the navigation corresponding to |response| should
    // be allowed to continue. Defaults to PolicyDecision::Allow() if not
    // overridden. Called before WebStateObserver::DidFinishNavigation. Calls
    // |callback| with the decision.
    // Never called in the following cases:
    //  - same-document navigations (unless initiated via LoadURLWithParams)
    //  - going back after form submission navigation
    //  - user-initiated POST navigation on iOS 10
    virtual void ShouldAllowResponse(NSURLResponse* response,
                                     ResponseInfo response_info,
                                     PolicyDecisionCallback callback);
  
    // Notifies the policy decider that the web state is being destroyed.
    // Gives subclasses a chance to cleanup.
    // The policy decider must not be destroyed while in this call, as removing
    // while iterating is not supported.
    virtual void WebStateDestroyed() {}
  
    WebState* web_state() const { return web_state_; }
  
   protected:
    // Designated constructor. Subscribes to |web_state|.
    explicit WebStatePolicyDecider(WebState* web_state);
  
   private:
    friend class WebStateImpl;
    friend class FakeWebState;
  
    // Resets the current web state.
    void ResetWebState();
  
    // The web state to decide navigation policy for.
    WebState* web_state_;
  };
  }  // namespace web
  
  #endif  // IOS_WEB_PUBLIC_NAVIGATION_WEB_STATE_POLICY_DECIDER_H_
  