chromium/chrome/browser/android/compositor/scene_layer/contextual_search_scene_layer.cc

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

#include "chrome/browser/android/compositor/scene_layer/contextual_search_scene_layer.h"

#include "base/android/jni_android.h"
#include "base/android/jni_array.h"
#include "base/android/jni_string.h"
#include "cc/slim/layer.h"
#include "cc/slim/solid_color_layer.h"
#include "chrome/browser/android/compositor/layer/contextual_search_layer.h"
#include "chrome/browser/profiles/profile.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/browser/web_contents.h"
#include "net/base/load_flags.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
#include "net/url_request/referrer_policy.h"
#include "services/network/public/mojom/url_loader_factory.mojom.h"
#include "ui/android/resources/resource_manager_impl.h"
#include "ui/android/view_android.h"
#include "ui/gfx/android/java_bitmap.h"
#include "ui/gfx/geometry/size_conversions.h"

// Must come after all headers that specialize FromJniType() / ToJniType().
#include "chrome/android/chrome_jni_headers/ContextualSearchSceneLayer_jni.h"

using base::android::JavaParamRef;
using base::android::JavaRef;

namespace android {

ContextualSearchSceneLayer::ContextualSearchSceneLayer(
    JNIEnv* env,
    const JavaRef<jobject>& jobj)
    : SceneLayer(env, jobj),
      env_(env),
      object_(jobj),
      color_overlay_(cc::slim::SolidColorLayer::Create()),
      content_container_(cc::slim::Layer::Create()) {
  // Responsible for moving the base page without modifying the layer itself.
  content_container_->SetIsDrawable(true);
  content_container_->SetPosition(gfx::PointF(0.0f, 0.0f));
  layer()->AddChild(content_container_);

  color_overlay_->SetIsDrawable(true);
  color_overlay_->SetOpacity(0.0f);
  color_overlay_->SetBackgroundColor(SkColors::kBlack);
  color_overlay_->SetPosition(gfx::PointF(0.f, 0.f));
  layer()->AddChild(color_overlay_);
}

void ContextualSearchSceneLayer::CreateContextualSearchLayer(
    JNIEnv* env,
    const JavaParamRef<jobject>& object,
    const JavaParamRef<jobject>& jresource_manager) {
  ui::ResourceManager* resource_manager =
      ui::ResourceManagerImpl::FromJavaObject(jresource_manager);
  contextual_search_layer_ = ContextualSearchLayer::Create(resource_manager);

  // The Contextual Search layer is initially invisible.
  contextual_search_layer_->layer()->SetHideLayerAndSubtree(true);

  layer()->AddChild(contextual_search_layer_->layer());
}

ContextualSearchSceneLayer::~ContextualSearchSceneLayer() {}

void ContextualSearchSceneLayer::UpdateContextualSearchLayer(
    JNIEnv* env,
    const JavaParamRef<jobject>& object,
    jint search_bar_background_resource_id,
    jint search_bar_background_color,
    jint search_context_resource_id,
    jint search_term_resource_id,
    jint search_caption_resource_id,
    jint search_bar_shadow_resource_id,
    jint search_provider_icon_resource_id,
    jint quick_action_icon_resource_id,
    jint drag_handlebar_resource_id,
    jint open_tab_icon_resource_id,
    jint close_icon_resource_id,
    jint progress_bar_background_resource_id,
    jint progress_bar_background_tint,
    jint progress_bar_resource_id,
    jint progress_bar_tint,
    jint search_promo_resource_id,
    jfloat dp_to_px,
    jfloat layout_width,
    jfloat layout_height,
    jfloat base_page_brightness,
    jfloat base_page_offset,
    const JavaParamRef<jobject>& jweb_contents,
    jboolean search_promo_visible,
    jfloat search_promo_height,
    jfloat search_promo_opacity,
    jint search_promo_background_color,
    // Related Searches
    jint related_searches_in_bar_resource_id,
    jboolean related_searches_in_bar_visible,
    jfloat related_searches_in_bar_height,
    jfloat related_searches_in_bar_redundant_padding,
    // Panel position etc
    jfloat search_panel_x,
    jfloat search_panel_y,
    jfloat search_panel_width,
    jfloat search_panel_height,
    jfloat search_bar_margin_side,
    jfloat search_bar_margin_top,
    jfloat search_bar_margin_bottom,
    jfloat search_bar_height,
    jfloat search_context_opacity,
    jfloat search_text_layer_min_height,
    jfloat search_term_opacity,
    jfloat search_term_caption_spacing,
    jfloat search_caption_animation_percentage,
    jboolean search_caption_visible,
    jboolean search_bar_border_visible,
    jfloat search_bar_border_height,
    jboolean quick_action_icon_visible,
    jboolean thumbnail_visible,
    jstring j_thumbnail_url,
    jfloat custom_image_visibility_percentage,
    jint bar_image_size,
    jint icon_color,
    jint drag_handlebar_color,
    jfloat close_icon_opacity,
    jboolean progress_bar_visible,
    jfloat progress_bar_height,
    jfloat progress_bar_opacity,
    jfloat progress_bar_completion,
    jboolean touch_highlight_visible,
    jfloat touch_highlight_x_offset,
    jfloat touch_highlight_width,
    Profile* profile,
    jint rounded_bar_top_resource_id,
    jint separator_line_color) {
  // Load the thumbnail if necessary.
  std::string thumbnail_url =
      base::android::ConvertJavaStringToUTF8(env, j_thumbnail_url);
  if (thumbnail_url != thumbnail_url_) {
    thumbnail_url_ = thumbnail_url;
    FetchThumbnail(profile);
  }

  // NOTE(pedrosimonetti): The WebContents might not exist at this time if
  // the Contextual Search Result has not been requested yet. In this case,
  // we'll pass NULL to Contextual Search's Layer Tree.
  content::WebContents* web_contents =
      content::WebContents::FromJavaWebContents(jweb_contents);

  scoped_refptr<cc::slim::Layer> content_layer =
      web_contents ? web_contents->GetNativeView()->GetLayer() : nullptr;

  // Fade the base page out.
  color_overlay_->SetOpacity(1.f - base_page_brightness);
  color_overlay_->SetBounds(
      gfx::ToCeiledSize(gfx::SizeF(layout_width, layout_height)));

  // Move the base page contents up.
  content_container_->SetPosition(gfx::PointF(0.0f, base_page_offset));

  contextual_search_layer_->SetProperties(
      search_bar_background_resource_id, search_bar_background_color,
      search_context_resource_id, search_term_resource_id,
      search_caption_resource_id, search_bar_shadow_resource_id,
      search_provider_icon_resource_id, quick_action_icon_resource_id,
      drag_handlebar_resource_id, open_tab_icon_resource_id,
      close_icon_resource_id, progress_bar_background_resource_id,
      progress_bar_background_tint, progress_bar_resource_id, progress_bar_tint,
      search_promo_resource_id, dp_to_px, content_layer, search_promo_visible,
      search_promo_height, search_promo_opacity, search_promo_background_color,
      // Related Searches
      related_searches_in_bar_resource_id, related_searches_in_bar_visible,
      related_searches_in_bar_height, related_searches_in_bar_redundant_padding,
      // Panel position etc
      search_panel_x, search_panel_y, search_panel_width, search_panel_height,
      search_bar_margin_side, search_bar_margin_top, search_bar_margin_bottom,
      search_bar_height, search_context_opacity, search_text_layer_min_height,
      search_term_opacity, search_term_caption_spacing,
      search_caption_animation_percentage, search_caption_visible,
      search_bar_border_visible, search_bar_border_height,
      quick_action_icon_visible, thumbnail_visible,
      custom_image_visibility_percentage, bar_image_size, icon_color,
      drag_handlebar_color, close_icon_opacity, progress_bar_visible,
      progress_bar_height, progress_bar_opacity, progress_bar_completion,
      touch_highlight_visible, touch_highlight_x_offset, touch_highlight_width,
      rounded_bar_top_resource_id, separator_line_color);

  // Make the layer visible if it is not already.
  contextual_search_layer_->layer()->SetHideLayerAndSubtree(false);
}

void ContextualSearchSceneLayer::FetchThumbnail(Profile* profile) {
  if (thumbnail_url_.empty())
    return;

  GURL gurl(thumbnail_url_);
  // Semantic details for this "Thumbnail" request.
  // The URLs processed access gstatic.com, which is considered a Google-owned
  // service.
  net::NetworkTrafficAnnotationTag traffic_annotation =
      net::DefineNetworkTrafficAnnotation("contextual_search_thumbnail",
                                          R"(
            semantics {
              sender: "Contextual Search"
              description:
                "This request is for a thumbnail image to show in the "
                "Contextual Search bottom sheet for an entity or similar "
                "object identified by the selected text."
              trigger:
                "Triggered by a server response to the "
                "contextual_search_resolve request which contains a thumbnail "
                "URL."
              data:
                "The URL of the thumbnail."
              destination: GOOGLE_OWNED_SERVICE
            }
            policy {
              cookies_allowed: NO
              setting:
                "This feature can be disabled by turning off 'Touch to Search' "
                "in Chrome for Android settings."
              chrome_policy {
                ContextualSearchEnabled {
                    policy_options {mode: MANDATORY}
                    ContextualSearchEnabled: false
                }
              }
            })");
  network::mojom::URLLoaderFactory* loader_factory =
      profile->GetDefaultStoragePartition()
          ->GetURLLoaderFactoryForBrowserProcess()
          .get();
  fetcher_ = std::make_unique<BitmapFetcher>(gurl, this, traffic_annotation);
  fetcher_->Init(
      net::ReferrerPolicy::REDUCE_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN,
      network::mojom::CredentialsMode::kOmit);
  fetcher_->Start(loader_factory);
}

void ContextualSearchSceneLayer::OnFetchComplete(const GURL& url,
                                                 const SkBitmap* bitmap) {
  bool success = bitmap && !bitmap->drawsNothing();
  Java_ContextualSearchSceneLayer_onThumbnailFetched(env_, object_, success);
  if (success)
    contextual_search_layer_->SetThumbnail(bitmap);

  fetcher_.reset();
}

void ContextualSearchSceneLayer::SetContentTree(
    JNIEnv* env,
    const JavaParamRef<jobject>& jobj,
    const JavaParamRef<jobject>& jcontent_tree) {
  SceneLayer* content_tree = FromJavaObject(env, jcontent_tree);
  if (!content_tree || !content_tree->layer())
    return;

  if (!content_tree->layer()->parent() ||
      (content_tree->layer()->parent()->id() != content_container_->id())) {
    content_container_->AddChild(content_tree->layer());
  }
}

void ContextualSearchSceneLayer::HideTree(JNIEnv* env,
                                          const JavaParamRef<jobject>& jobj) {
  // TODO(mdjones): Create super class for this logic.
  if (contextual_search_layer_) {
    contextual_search_layer_->layer()->SetHideLayerAndSubtree(true);
  }
  // Reset base page brightness.
  color_overlay_->SetOpacity(0.f);
  // Reset base page offset.
  content_container_->SetPosition(gfx::PointF(0.0f, 0.0f));
}

static jlong JNI_ContextualSearchSceneLayer_Init(
    JNIEnv* env,
    const JavaParamRef<jobject>& jobj) {
  // This will automatically bind to the Java object and pass ownership there.
  ContextualSearchSceneLayer* tree_provider =
      new ContextualSearchSceneLayer(env, jobj);
  return reinterpret_cast<intptr_t>(tree_provider);
}

}  // namespace android