// Copyright 2014 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 CONTENT_BROWSER_FRAME_HOST_CROSS_PROCESS_FRAME_CONNECTOR_H_
#define CONTENT_BROWSER_FRAME_HOST_CROSS_PROCESS_FRAME_CONNECTOR_H_

#include <stdint.h>

#include "cc/input/touch_action.h"
#include "components/viz/common/quads/compositor_frame.h"
#include "components/viz/common/surfaces/local_surface_id.h"
#include "components/viz/common/surfaces/surface_id.h"
#include "content/browser/renderer_host/frame_connector_delegate.h"
#include "content/common/content_export.h"
#include "content/common/frame_visual_properties.h"

namespace IPC {
class Message;
}

namespace content {
class RenderFrameProxyHost;

// CrossProcessFrameConnector provides the platform view abstraction for
// RenderWidgetHostViewChildFrame allowing RWHVChildFrame to remain ignorant
// of RenderFrameHost.
//
// The RenderWidgetHostView of an out-of-process child frame needs to
// communicate with the RenderFrameProxyHost representing this frame in the
// process of the parent frame. For example, assume you have this page:
//
//   -----------------
//   | frame 1       |
//   |  -----------  |
//   |  | frame 2 |  |
//   |  -----------  |
//   -----------------
//
// If frames 1 and 2 are in process A and B, there are 4 hosts:
//   A1 - RFH for frame 1 in process A
//   B1 - RFPH for frame 1 in process B
//   A2 - RFPH for frame 2 in process A
//   B2 - RFH for frame 2 in process B
//
// B2, having a parent frame in a different process, will have a
// RenderWidgetHostViewChildFrame. This RenderWidgetHostViewChildFrame needs
// to communicate with A2 because the embedding frame represents the platform
// that the child frame is rendering into -- it needs information necessary for
// compositing child frame textures, and also can pass platform messages such as
// view resizing. CrossProcessFrameConnector bridges between B2's
// RenderWidgetHostViewChildFrame and A2 to allow for this communication.
// (Note: B1 is only mentioned for completeness. It is not needed in this
// example.)
//
// CrossProcessFrameConnector objects are owned by the RenderFrameProxyHost
// in the child frame's RenderFrameHostManager corresponding to the parent's
// SiteInstance, A2 in the picture above. When a child frame navigates in a new
// process, SetView() is called to update to the new view.
//
class CONTENT_EXPORT CrossProcessFrameConnector
    : public FrameConnectorDelegate {
 public:
  // |frame_proxy_in_parent_renderer| corresponds to A2 in the example above.
  explicit CrossProcessFrameConnector(
      RenderFrameProxyHost* frame_proxy_in_parent_renderer);
  ~CrossProcessFrameConnector() override;

  bool OnMessageReceived(const IPC::Message &msg);

  // |view| corresponds to B2's RenderWidgetHostViewChildFrame in the example
  // above.
  RenderWidgetHostViewChildFrame* get_view_for_testing() { return view_; }

  // FrameConnectorDelegate implementation.
  void SetView(RenderWidgetHostViewChildFrame* view) override;
  RenderWidgetHostViewBase* GetParentRenderWidgetHostView() override;
  RenderWidgetHostViewBase* GetRootRenderWidgetHostView() override;
  void RenderProcessGone() override;
  void FirstSurfaceActivation(const viz::SurfaceInfo& surface_info) override;
  void SendIntrinsicSizingInfoToParent(
      const blink::WebIntrinsicSizingInfo&) override;

  void UpdateCursor(const WebCursor& cursor) override;
  gfx::PointF TransformPointToRootCoordSpace(
      const gfx::PointF& point,
      const viz::SurfaceId& surface_id) override;
  bool TransformPointToLocalCoordSpaceLegacy(
      const gfx::PointF& point,
      const viz::SurfaceId& original_surface,
      const viz::SurfaceId& local_surface_id,
      gfx::PointF* transformed_point) override;
  bool TransformPointToCoordSpaceForView(
      const gfx::PointF& point,
      RenderWidgetHostViewBase* target_view,
      const viz::SurfaceId& local_surface_id,
      gfx::PointF* transformed_point,
      viz::EventSource source = viz::EventSource::ANY) override;
  void ForwardAckedTouchpadPinchGestureEvent(
      const blink::WebGestureEvent& event,
      InputEventAckState ack_result) override;
  void BubbleScrollEvent(const blink::WebGestureEvent& event) override;
  bool HasFocus() override;
  void FocusRootView() override;
  bool LockMouse() override;
  void UnlockMouse() override;
  void EnableAutoResize(const gfx::Size& min_size,
                        const gfx::Size& max_size) override;
  void DisableAutoResize() override;
  bool IsInert() const override;
  cc::TouchAction InheritedEffectiveTouchAction() const override;
  bool IsHidden() const override;
  bool IsThrottled() const override;
  bool IsSubtreeThrottled() const override;
#if defined(USE_AURA)
  void EmbedRendererWindowTreeClientInParent(
      ws::mojom::WindowTreeClientPtr window_tree_client) override;
#endif
  void DidUpdateVisualProperties(
      const cc::RenderFrameMetadata& metadata) override;

  // Set the visibility of immediate child views, i.e. views whose parent view
  // is |view_|.
  void SetVisibilityForChildViews(bool visible) const override;

  void SetScreenSpaceRect(const gfx::Rect& screen_space_rect) override;

  // Exposed for tests.
  RenderWidgetHostViewBase* GetRootRenderWidgetHostViewForTesting() {
    return GetRootRenderWidgetHostView();
  }

  // This enum backs a histogram - please do not modify or remove the existing
  // enum values below (adding new values is okay, but please remember to also
  // update enums.xml in this case). See enums.xml for descriptions of enum
  // values.
  enum class CrashVisibility {
    kCrashedWhileVisible = 0,
    kShownAfterCrashing = 1,
    kNeverVisibleAfterCrash = 2,
    kMaxValue = kNeverVisibleAfterCrash
  };
  // Logs the Stability.ChildFrameCrash.Visibility metric after checking that a
  // crash has indeed happened and checking that the crash has not already been
  // logged in UMA.
  void MaybeLogCrash(CrashVisibility visibility);

  // Returns whether the child widget is actually visible to the user.  This is
  // different from the IsHidden override, and takes into account viewport
  // intersection as well as the visibility of the RenderFrameHostDelegate.
  bool IsVisible();

 private:
  friend class MockCrossProcessFrameConnector;

  // Resets the rect and the viz::LocalSurfaceId of the connector to ensure the
  // unguessable surface ID is not reused after a cross-process navigation.
  void ResetScreenSpaceRect();

  // Handlers for messages received from the parent frame.
  void OnSynchronizeVisualProperties(
      const viz::SurfaceId& surface_id,
      const FrameVisualProperties& visual_properties);
  void OnUpdateViewportIntersection(const gfx::Rect& viewport_intersection,
                                    const gfx::Rect& compositor_visible_rect,
                                    bool occluded_or_obscured);
  void OnVisibilityChanged(bool visible);
  void OnSetIsInert(bool);
  void OnSetInheritedEffectiveTouchAction(cc::TouchAction);
  void OnUpdateRenderThrottlingStatus(bool is_throttled,
                                      bool subtree_throttled);

  // The RenderFrameProxyHost that routes messages to the parent frame's
  // renderer process.
  RenderFrameProxyHost* frame_proxy_in_parent_renderer_;

  bool is_inert_ = false;
  cc::TouchAction inherited_effective_touch_action_ =
      cc::TouchAction::kTouchActionAuto;

  bool is_throttled_ = false;
  bool subtree_throttled_ = false;

  // Visibility state of the corresponding frame owner element in parent process
  // which is set through CSS.
  bool is_hidden_ = false;

  bool is_scroll_bubbling_;

  // Used to make sure we only log UMA once per renderer crash.
  bool is_crash_already_logged_ = false;

  // Used to make sure that MaybeLogCrash only logs the UMA in case of an actual
  // crash (in case it is called from the destructor of
  // CrossProcessFrameConnector or when WebContentsImpl::WasShown is called).
  bool has_crashed_ = false;

  // The last pre-transform frame size received from the parent renderer.
  // |last_received_local_frame_size_| may be in DIP if use zoom for DSF is
  // off.
  gfx::Size last_received_local_frame_size_;

  // The last zoom level received from parent renderer, which is used to check
  // if a new surface is created in case of zoom level change.
  double last_received_zoom_level_ = 0.0;

  DISALLOW_COPY_AND_ASSIGN(CrossProcessFrameConnector);
};

}  // namespace content

#endif  // CONTENT_BROWSER_FRAME_HOST_CROSS_PROCESS_FRAME_CONNECTOR_H_
