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

#ifndef CONTENT_BROWSER_WEB_EXPOSED_ISOLATION_INFO_H_
#define CONTENT_BROWSER_WEB_EXPOSED_ISOLATION_INFO_H_

#include <optional>

#include "base/tracing/protos/chrome_track_event.pbzero.h"
#include "content/common/content_export.h"
#include "third_party/perfetto/include/perfetto/tracing/traced_proto.h"
#include "url/origin.h"

namespace content {

// Represents the isolation level of a page or group of pages. This is used
// for process allocation and to selectively enable powerful features such
// as SharedArrayBuffer and Direct Sockets.
//
// Currently, three levels of isolation are represented:
//
// 1.  Non-isolated contexts.
//
// 2.  Cross-origin isolation, as defined in
//     https://html.spec.whatwg.org/multipage/document-sequences.html#cross-origin-isolation-mode.
//     This is computed purely by examining Cross-Origin-Opener-Policy and
//     Cross-Origin-Embedder-Policy headers on a given response.
//
// 3.  Isolated Application contexts, which correspond to Isolated Contexts as
//     defined in:
//     https://wicg.github.io/isolated-web-apps/isolated-contexts.html
//     These contexts have higher isolation and integrity requirements than
//     cross-origin isolation. The embedder is responsible for deciding whether
//     a particular cross-origin isolated environment can qualify for this
//     isolation level.
class CONTENT_EXPORT WebExposedIsolationInfo {
 public:
  static WebExposedIsolationInfo CreateNonIsolated();
  static WebExposedIsolationInfo CreateIsolated(const url::Origin& origin);
  static WebExposedIsolationInfo CreateIsolatedApplication(
      const url::Origin& origin);

  // These helpers make it easy to compare against an optional
  // WebExposedIsolationInfo. This is useful because a navigation may return
  // an empty WebExposedIsolationInfo to the process model, for example when
  // we do not yet have a final network response. In that case it is considered
  // compatible with any WebExposedIsolationInfo.
  //
  // In details, the underlying logic is as follow:
  // - Two valid values are compared using the == operator.
  // - Null and a valid value returns true.
  // - Two null values returns true.
  static bool AreCompatible(const WebExposedIsolationInfo& a,
                            const WebExposedIsolationInfo& b);
  static bool AreCompatible(const WebExposedIsolationInfo& a,
                            const std::optional<WebExposedIsolationInfo>& b);
  static bool AreCompatible(const std::optional<WebExposedIsolationInfo>& a,
                            const WebExposedIsolationInfo& b);
  static bool AreCompatible(const std::optional<WebExposedIsolationInfo>& a,
                            const std::optional<WebExposedIsolationInfo>& b);

  WebExposedIsolationInfo(const WebExposedIsolationInfo& other);
  ~WebExposedIsolationInfo();

  // Returns `true` for isolated contexts created via `CreateIsolated()` or
  // `CreateIsolatedApplication()`, and false for contexts created via
  // `CreateNonIsolated()`.
  //
  // This corresponds to "cross-origin isolation" as defined in HTML:
  // https://html.spec.whatwg.org/multipage/document-sequences.html#cross-origin-isolation-mode
  bool is_isolated() const { return origin_.has_value(); }

  // Returns `true` for contexts created via `CreateIsolatedApplication()`, and
  // `false` for those created via `CreateNonIsolated()` or `CreatedIsolated()`.
  //
  // This corresponds to "isolated contexts" as defined here:
  // https://wicg.github.io/isolated-web-apps/isolated-contexts.html
  bool is_isolated_application() const {
    return origin_.has_value() && isolated_application_;
  }

  // Returns the top level origin shared across pages with this cross-origin
  // isolation status. This only returns a value if is_isolated is true.
  const url::Origin& origin() const;

  bool operator==(const WebExposedIsolationInfo& b) const;

  // Non-isolated < Isolated < Isolated Application.
  //
  // All non-isolated contexts are equivalent.
  //
  // Origin comparisons determine ordering of isolated contexts.
  bool operator<(const WebExposedIsolationInfo& b) const;

  using TraceProto = perfetto::protos::pbzero::WebExposedIsolationInfo;
  void WriteIntoTrace(perfetto::TracedProto<TraceProto> proto) const;

 private:
  WebExposedIsolationInfo(const std::optional<url::Origin>& origin,
                          bool isolated_application);

  // |origin_| serve two purposes. If null, it indicates that the page(s) it
  // refers to are not isolated, and that the crossOriginIsolated boolean is
  // false. If it has a value, all these page(s) share the same top level
  // origin. This ensure we can put them in the same process.
  std::optional<url::Origin> origin_;

  // Some applications may require additional isolation above and beyond what
  // COOP/COEP-based COI provides. This boolean will be `true` for applications
  // that have opted into such a context.
  //
  // TODO(mkwst): Improve the description of the Isolated Application context as
  // we work out what it is: https://crbug.com/1206150.
  bool isolated_application_ = false;
};

CONTENT_EXPORT std::ostream& operator<<(std::ostream& out,
                                        const WebExposedIsolationInfo& info);

// Disable these operators, AreCompatible() functions should be used instead.
bool operator==(const std::optional<WebExposedIsolationInfo>& a,
                const std::optional<WebExposedIsolationInfo>& b) = delete;
bool operator==(const WebExposedIsolationInfo& a,
                const std::optional<WebExposedIsolationInfo>& b) = delete;
bool operator==(const std::optional<WebExposedIsolationInfo>& a,
                const WebExposedIsolationInfo& b) = delete;
bool operator!=(const std::optional<WebExposedIsolationInfo>& a,
                const std::optional<WebExposedIsolationInfo>& b) = delete;
bool operator!=(const WebExposedIsolationInfo& a,
                const std::optional<WebExposedIsolationInfo>& b) = delete;
bool operator!=(const std::optional<WebExposedIsolationInfo>& a,
                const WebExposedIsolationInfo& b) = delete;

}  // namespace content

#endif  // CONTENT_BROWSER_WEB_EXPOSED_ISOLATION_INFO_H_
