Send occlusion fraction to Wayland clients.

  is sent to the Android side.

Bug: b/112668686
Bug: b/110011377
Test: Occlusion fraction changes upon remote surface losing focus and
Change-Id: Id622509a1eb504db945eb7c0b6c6db5256103d4d
Reviewed-on: https://chromium-review.googlesource.com/c/1179560
Commit-Queue: Eliot Courtney <edcourtney@chromium.org>
Reviewed-by: Mitsuru Oshima (gardener - slow) <oshima@chromium.org>
Cr-Commit-Position: refs/heads/master@{#616923}
diff --git a/components/exo/surface.cc b/components/exo/surface.cc
index c50f3fa..8f370bf 100644
--- a/components/exo/surface.cc
+++ b/components/exo/surface.cc
@@ -30,6 +30,7 @@
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/client/drag_drop_delegate.h"
 #include "ui/aura/window_delegate.h"
+#include "ui/aura/window_occlusion_tracker.h"
 #include "ui/aura/window_targeter.h"
 #include "ui/base/class_property.h"
 #include "ui/base/cursor/cursor.h"
@@ -141,6 +142,10 @@
   void OnWindowDestroying(aura::Window* window) override {}
   void OnWindowDestroyed(aura::Window* window) override { delete this; }
   void OnWindowTargetVisibilityChanged(bool visible) override {}
+  void OnWindowOcclusionChanged(aura::Window::OcclusionState occlusion_state,
+                                const SkRegion& occluded_region) override {
+    surface_->OnWindowOcclusionChanged();
+  }
   bool HasHitTestMask() const override { return true; }
   void GetHitTestMask(gfx::Path* mask) const override {
     surface_->GetHitTestMask(mask);
@@ -731,6 +736,15 @@
          state_.opaque_region.Contains(gfx::Rect(content_size_));
 }
 
+void Surface::SetOcclusionTracking(bool tracking) {
+  is_tracking_occlusion_ = tracking;
+  // TODO(edcourtney): Currently, it doesn't seem to be possible to stop
+  // tracking the occlusion state once started, but it would be nice to stop if
+  // the tracked occlusion region becomes empty.
+  if (is_tracking_occlusion_)
+    window()->TrackOcclusionState();
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // Buffer, private:
 
@@ -953,4 +967,12 @@
   }
 }
 
+void Surface::OnWindowOcclusionChanged() {
+  if (!is_tracking_occlusion_)
+    return;
+
+  for (SurfaceObserver& observer : observers_)
+    observer.OnWindowOcclusionChanged(this);
+}
+
 }  // namespace exo
diff --git a/components/exo/surface.h b/components/exo/surface.h
index 4ac3aba..0ee0827 100644
--- a/components/exo/surface.h
+++ b/components/exo/surface.h
@@ -240,6 +240,12 @@
     return pending_damage_.Contains(damage);
   }
 
+  // Set occlusion tracking region for surface.
+  void SetOcclusionTracking(bool tracking);
+
+  // Triggers sending an occlusion update to observers.
+  void OnWindowOcclusionChanged();
+
  private:
   struct State {
     State();
@@ -394,6 +400,9 @@
   // Surface observer list. Surface does not own the observers.
   base::ObserverList<SurfaceObserver, true>::Unchecked observers_;
 
+  // Whether this surface is tracking occlusion for the client.
+  bool is_tracking_occlusion_ = false;
+
   DISALLOW_COPY_AND_ASSIGN(Surface);
 };
 
diff --git a/components/exo/surface_observer.h b/components/exo/surface_observer.h
index dd27072..69c5a09 100644
--- a/components/exo/surface_observer.h
+++ b/components/exo/surface_observer.h
@@ -15,6 +15,10 @@
   // chance to remove themselves.
   virtual void OnSurfaceDestroying(Surface* surface) = 0;
 
+  // Called when the occlusion of the aura window corresponding to |surface|
+  // changes.
+  virtual void OnWindowOcclusionChanged(Surface* surface) {}
+
  protected:
   virtual ~SurfaceObserver() {}
 };
diff --git a/components/exo/wayland/protocol/aura-shell.xml b/components/exo/wayland/protocol/aura-shell.xml
index bd74287..5c95da5 100644
--- a/components/exo/wayland/protocol/aura-shell.xml
+++ b/components/exo/wayland/protocol/aura-shell.xml
@@ -24,7 +24,7 @@
     DEALINGS IN THE SOFTWARE.
   </copyright>
 
-  <interface name="zaura_shell" version="7">
+  <interface name="zaura_shell" version="8">
     <description summary="aura_shell">
       The global interface exposing aura shell capabilities is used to
       instantiate an interface extension for a wl_surface object.
@@ -68,7 +68,7 @@
     </request>
   </interface>
 
-  <interface name="zaura_surface" version="7">
+  <interface name="zaura_surface" version="8">
     <description summary="aura shell interface to a wl_surface">
       An additional interface to a wl_surface object, which allows the
       client to access aura shell specific functionality for surface.
@@ -138,6 +138,40 @@
       </description>
       <arg name="client_surface_id" type="int" />
     </request>
+
+    <!-- Version 8 additions -->
+
+    <enum name="occlusion_change_reason">
+      <description summary="occlusion change reason">
+	Enum describing why an occlusion change happened. An occlusion change as a
+	result of a user action could include things like the user moving a window,
+	changing occlusion, or opening/closing a window, changing the occlusion.
+      </description>
+      <entry name="user_action" value="1" summary="occlusion changed as a result of a user action"/>
+    </enum>
+
+    <request name="set_occlusion_tracking" since="8">
+      <description summary="set tracked occlusion region">
+	Sets occlusion tracking on this surface. The client will be updated with a
+	new occlusion fraction when the amount of occlusion of this surface changes.
+      </description>
+    </request>
+
+    <request name="unset_occlusion_tracking" since="8">
+      <description summary="unset tracked occlusion region">
+	Unsets occlusion tracking for this surface.
+      </description>
+    </request>
+
+    <event name="occlusion_changed" since="8">
+      <description summary="Notifies on an occlusion change">
+	Notifies when there is a change in the amount this surface is occluded.
+	The occlusion update is sent as a fixed point number from 0 to 1, representing
+	the proportion of occlusion.
+      </description>
+      <arg name="occlusion_fraction" type="fixed"/>
+      <arg name="occlusion_reason" type="uint"/>
+    </event>
   </interface>
 
   <interface name="zaura_output" version="6">
diff --git a/components/exo/wayland/public/aura-shell-client-protocol.h b/components/exo/wayland/public/aura-shell-client-protocol.h
index 1fdc03b..e5b5b76 100644
--- a/components/exo/wayland/public/aura-shell-client-protocol.h
+++ b/components/exo/wayland/public/aura-shell-client-protocol.h
@@ -215,13 +215,67 @@
 };
 #endif /* ZAURA_SURFACE_FRAME_TYPE_ENUM */
 
+#ifndef ZAURA_SURFACE_OCCLUSION_CHANGE_REASON_ENUM
+#define ZAURA_SURFACE_OCCLUSION_CHANGE_REASON_ENUM
+/**
+ * @ingroup iface_zaura_surface
+ * occlusion change reason
+ *
+ * Enum describing why an occlusion change happened. An occlusion change as a
+ * result of a user action could include things like the user moving a window,
+ * changing occlusion, or opening/closing a window, changing the occlusion.
+ */
+enum zaura_surface_occlusion_change_reason {
+	/**
+	 * occlusion changed as a result of a user action
+	 */
+	ZAURA_SURFACE_OCCLUSION_CHANGE_REASON_USER_ACTION = 1,
+};
+#endif /* ZAURA_SURFACE_OCCLUSION_CHANGE_REASON_ENUM */
+
+/**
+ * @ingroup iface_zaura_surface
+ * @struct zaura_surface_listener
+ */
+struct zaura_surface_listener {
+	/**
+	 * Notifies on an occlusion change
+	 *
+	 * Notifies when there is a change in the amount this surface is
+	 * occluded. The occlusion update is sent as a fixed point number
+	 * from 0 to 1, representing the proportion of occlusion.
+	 * @since 8
+	 */
+	void (*occlusion_changed)(void *data,
+				  struct zaura_surface *zaura_surface,
+				  wl_fixed_t occlusion_fraction,
+				  uint32_t occlusion_reason);
+};
+
+/**
+ * @ingroup iface_zaura_surface
+ */
+static inline int
+zaura_surface_add_listener(struct zaura_surface *zaura_surface,
+			   const struct zaura_surface_listener *listener, void *data)
+{
+	return wl_proxy_add_listener((struct wl_proxy *) zaura_surface,
+				     (void (**)(void)) listener, data);
+}
+
 #define ZAURA_SURFACE_SET_FRAME 0
 #define ZAURA_SURFACE_SET_PARENT 1
 #define ZAURA_SURFACE_SET_FRAME_COLORS 2
 #define ZAURA_SURFACE_SET_STARTUP_ID 3
 #define ZAURA_SURFACE_SET_APPLICATION_ID 4
 #define ZAURA_SURFACE_SET_CLIENT_SURFACE_ID 5
+#define ZAURA_SURFACE_SET_OCCLUSION_TRACKING 6
+#define ZAURA_SURFACE_UNSET_OCCLUSION_TRACKING 7
 
+/**
+ * @ingroup iface_zaura_surface
+ */
+#define ZAURA_SURFACE_OCCLUSION_CHANGED_SINCE_VERSION 8
 
 /**
  * @ingroup iface_zaura_surface
@@ -247,6 +301,14 @@
  * @ingroup iface_zaura_surface
  */
 #define ZAURA_SURFACE_SET_CLIENT_SURFACE_ID_SINCE_VERSION 7
+/**
+ * @ingroup iface_zaura_surface
+ */
+#define ZAURA_SURFACE_SET_OCCLUSION_TRACKING_SINCE_VERSION 8
+/**
+ * @ingroup iface_zaura_surface
+ */
+#define ZAURA_SURFACE_UNSET_OCCLUSION_TRACKING_SINCE_VERSION 8
 
 /** @ingroup iface_zaura_surface */
 static inline void
@@ -348,6 +410,31 @@
 			 ZAURA_SURFACE_SET_CLIENT_SURFACE_ID, client_surface_id);
 }
 
+/**
+ * @ingroup iface_zaura_surface
+ *
+ * Sets occlusion tracking on this surface. The client will be updated with a
+ * new occlusion fraction when the amount of occlusion of this surface changes.
+ */
+static inline void
+zaura_surface_set_occlusion_tracking(struct zaura_surface *zaura_surface)
+{
+	wl_proxy_marshal((struct wl_proxy *) zaura_surface,
+			 ZAURA_SURFACE_SET_OCCLUSION_TRACKING);
+}
+
+/**
+ * @ingroup iface_zaura_surface
+ *
+ * Unsets occlusion tracking for this surface.
+ */
+static inline void
+zaura_surface_unset_occlusion_tracking(struct zaura_surface *zaura_surface)
+{
+	wl_proxy_marshal((struct wl_proxy *) zaura_surface,
+			 ZAURA_SURFACE_UNSET_OCCLUSION_TRACKING);
+}
+
 #ifndef ZAURA_OUTPUT_SCALE_PROPERTY_ENUM
 #define ZAURA_OUTPUT_SCALE_PROPERTY_ENUM
 /**
diff --git a/components/exo/wayland/public/aura-shell-protocol.c b/components/exo/wayland/public/aura-shell-protocol.c
index 0a04918..299c22c 100644
--- a/components/exo/wayland/public/aura-shell-protocol.c
+++ b/components/exo/wayland/public/aura-shell-protocol.c
@@ -50,7 +50,7 @@
 };
 
 WL_EXPORT const struct wl_interface zaura_shell_interface = {
-	"zaura_shell", 7,
+	"zaura_shell", 8,
 	2, zaura_shell_requests,
 	0, NULL,
 };
@@ -62,12 +62,18 @@
 	{ "set_startup_id", "4?s", types + 0 },
 	{ "set_application_id", "5?s", types + 0 },
 	{ "set_client_surface_id", "7i", types + 0 },
+	{ "set_occlusion_tracking", "8", types + 0 },
+	{ "unset_occlusion_tracking", "8", types + 0 },
+};
+
+static const struct wl_message zaura_surface_events[] = {
+	{ "occlusion_changed", "8fu", types + 0 },
 };
 
 WL_EXPORT const struct wl_interface zaura_surface_interface = {
-	"zaura_surface", 7,
-	6, zaura_surface_requests,
-	0, NULL,
+	"zaura_surface", 8,
+	8, zaura_surface_requests,
+	1, zaura_surface_events,
 };
 
 static const struct wl_message zaura_output_events[] = {
diff --git a/components/exo/wayland/public/aura-shell-server-protocol.h b/components/exo/wayland/public/aura-shell-server-protocol.h
index 1b7d4690..25c7855 100644
--- a/components/exo/wayland/public/aura-shell-server-protocol.h
+++ b/components/exo/wayland/public/aura-shell-server-protocol.h
@@ -186,6 +186,24 @@
 };
 #endif /* ZAURA_SURFACE_FRAME_TYPE_ENUM */
 
+#ifndef ZAURA_SURFACE_OCCLUSION_CHANGE_REASON_ENUM
+#define ZAURA_SURFACE_OCCLUSION_CHANGE_REASON_ENUM
+/**
+ * @ingroup iface_zaura_surface
+ * occlusion change reason
+ *
+ * Enum describing why an occlusion change happened. An occlusion change as a
+ * result of a user action could include things like the user moving a window,
+ * changing occlusion, or opening/closing a window, changing the occlusion.
+ */
+enum zaura_surface_occlusion_change_reason {
+	/**
+	 * occlusion changed as a result of a user action
+	 */
+	ZAURA_SURFACE_OCCLUSION_CHANGE_REASON_USER_ACTION = 1,
+};
+#endif /* ZAURA_SURFACE_OCCLUSION_CHANGE_REASON_ENUM */
+
 /**
  * @ingroup iface_zaura_surface
  * @struct zaura_surface_interface
@@ -251,8 +269,32 @@
 	void (*set_client_surface_id)(struct wl_client *client,
 				      struct wl_resource *resource,
 				      int32_t client_surface_id);
+	/**
+	 * set tracked occlusion region
+	 *
+	 * Sets occlusion tracking on this surface. The client will be
+	 * updated with a new occlusion fraction when the amount of
+	 * occlusion of this surface changes.
+	 * @since 8
+	 */
+	void (*set_occlusion_tracking)(struct wl_client *client,
+				       struct wl_resource *resource);
+	/**
+	 * unset tracked occlusion region
+	 *
+	 * Unsets occlusion tracking for this surface.
+	 * @since 8
+	 */
+	void (*unset_occlusion_tracking)(struct wl_client *client,
+					 struct wl_resource *resource);
 };
 
+#define ZAURA_SURFACE_OCCLUSION_CHANGED 0
+
+/**
+ * @ingroup iface_zaura_surface
+ */
+#define ZAURA_SURFACE_OCCLUSION_CHANGED_SINCE_VERSION 8
 
 /**
  * @ingroup iface_zaura_surface
@@ -278,6 +320,25 @@
  * @ingroup iface_zaura_surface
  */
 #define ZAURA_SURFACE_SET_CLIENT_SURFACE_ID_SINCE_VERSION 7
+/**
+ * @ingroup iface_zaura_surface
+ */
+#define ZAURA_SURFACE_SET_OCCLUSION_TRACKING_SINCE_VERSION 8
+/**
+ * @ingroup iface_zaura_surface
+ */
+#define ZAURA_SURFACE_UNSET_OCCLUSION_TRACKING_SINCE_VERSION 8
+
+/**
+ * @ingroup iface_zaura_surface
+ * Sends an occlusion_changed event to the client owning the resource.
+ * @param resource_ The client's resource
+ */
+static inline void
+zaura_surface_send_occlusion_changed(struct wl_resource *resource_, wl_fixed_t occlusion_fraction, uint32_t occlusion_reason)
+{
+	wl_resource_post_event(resource_, ZAURA_SURFACE_OCCLUSION_CHANGED, occlusion_fraction, occlusion_reason);
+}
 
 #ifndef ZAURA_OUTPUT_SCALE_PROPERTY_ENUM
 #define ZAURA_OUTPUT_SCALE_PROPERTY_ENUM
diff --git a/components/exo/wayland/zaura_shell.cc b/components/exo/wayland/zaura_shell.cc
index 610b140e..560dcf8 100644
--- a/components/exo/wayland/zaura_shell.cc
+++ b/components/exo/wayland/zaura_shell.cc
@@ -30,7 +30,8 @@
 
 class AuraSurface : public SurfaceObserver {
  public:
-  explicit AuraSurface(Surface* surface) : surface_(surface) {
+  AuraSurface(Surface* surface, wl_resource* resource)
+      : surface_(surface), resource_(resource) {
     surface_->AddSurfaceObserver(this);
     surface_->SetProperty(kSurfaceHasAuraSurfaceKey, true);
   }
@@ -72,14 +73,59 @@
       surface_->SetClientSurfaceId(client_surface_id);
   }
 
+  void SetOcclusionTracking(bool tracking) {
+    if (surface_)
+      surface_->SetOcclusionTracking(tracking);
+  }
+
   // Overridden from SurfaceObserver:
   void OnSurfaceDestroying(Surface* surface) override {
     surface->RemoveSurfaceObserver(this);
     surface_ = nullptr;
   }
 
+  void OnWindowOcclusionChanged(Surface* surface) override {
+    if (wl_resource_get_version(resource_) < 8)
+      return;
+
+    auto* window = surface->window();
+    const gfx::Rect bounds_in_screen = window->GetBoundsInScreen();
+    const int tracked_area =
+        bounds_in_screen.width() * bounds_in_screen.height();
+    int occluded_area = 0;
+    switch (window->occlusion_state()) {
+      case aura::Window::OcclusionState::VISIBLE: {
+        SkRegion tracked_and_occluded_region = window->occluded_region();
+        tracked_and_occluded_region.op(gfx::RectToSkIRect(bounds_in_screen),
+                                       SkRegion::Op::kIntersect_Op);
+        for (SkRegion::Iterator i(tracked_and_occluded_region); !i.done();
+             i.next()) {
+          occluded_area += i.rect().width() * i.rect().height();
+        }
+        break;
+      }
+      case aura::Window::OcclusionState::OCCLUDED:
+      case aura::Window::OcclusionState::HIDDEN:
+        occluded_area = tracked_area;
+        break;
+      case aura::Window::OcclusionState::UNKNOWN:
+        return;  // Window is not tracked.
+    }
+
+    const float fraction_occluded =
+        static_cast<float>(occluded_area) / static_cast<float>(tracked_area);
+
+    // TODO(edcourtney): For now, we are treating every occlusion change as
+    // from a user action.
+    zaura_surface_send_occlusion_changed(
+        resource_, wl_fixed_from_double(fraction_occluded),
+        ZAURA_SURFACE_OCCLUSION_CHANGE_REASON_USER_ACTION);
+    wl_client_flush(wl_resource_get_client(resource_));
+  }
+
  private:
   Surface* surface_;
+  wl_resource* const resource_;
 
   DISALLOW_COPY_AND_ASSIGN(AuraSurface);
 };
@@ -140,10 +186,25 @@
   GetUserDataAs<AuraSurface>(resource)->SetClientSurfaceId(client_surface_id);
 }
 
+void aura_surface_set_occlusion_tracking(wl_client* client,
+                                         wl_resource* resource) {
+  GetUserDataAs<AuraSurface>(resource)->SetOcclusionTracking(true);
+}
+
+void aura_surface_unset_occlusion_tracking(wl_client* client,
+                                           wl_resource* resource) {
+  GetUserDataAs<AuraSurface>(resource)->SetOcclusionTracking(false);
+}
+
 const struct zaura_surface_interface aura_surface_implementation = {
-    aura_surface_set_frame,          aura_surface_set_parent,
-    aura_surface_set_frame_colors,   aura_surface_set_startup_id,
-    aura_surface_set_application_id, aura_surface_set_client_surface_id};
+    aura_surface_set_frame,
+    aura_surface_set_parent,
+    aura_surface_set_frame_colors,
+    aura_surface_set_startup_id,
+    aura_surface_set_application_id,
+    aura_surface_set_client_surface_id,
+    aura_surface_set_occlusion_tracking,
+    aura_surface_unset_occlusion_tracking};
 
 ////////////////////////////////////////////////////////////////////////////////
 // aura_output_interface:
@@ -236,8 +297,9 @@
   wl_resource* aura_surface_resource = wl_resource_create(
       client, &zaura_surface_interface, wl_resource_get_version(resource), id);
 
-  SetImplementation(aura_surface_resource, &aura_surface_implementation,
-                    std::make_unique<AuraSurface>(surface));
+  SetImplementation(
+      aura_surface_resource, &aura_surface_implementation,
+      std::make_unique<AuraSurface>(surface, aura_surface_resource));
 }
 
 void aura_shell_get_aura_output(wl_client* client,
diff --git a/components/exo/wayland/zaura_shell.h b/components/exo/wayland/zaura_shell.h
index 42daf74..0bc1bc6 100644
--- a/components/exo/wayland/zaura_shell.h
+++ b/components/exo/wayland/zaura_shell.h
@@ -12,7 +12,7 @@
 namespace exo {
 namespace wayland {
 
-constexpr uint32_t kZAuraShellVersion = 7;
+constexpr uint32_t kZAuraShellVersion = 8;
 
 void bind_aura_shell(wl_client* client,
                      void* data,