File: contextual_search_layer.cc

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (647 lines) | stat: -rw-r--r-- 27,492 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
// Copyright 2014 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/layer/contextual_search_layer.h"

#include "cc/resources/scoped_ui_resource.h"
#include "cc/slim/layer.h"
#include "cc/slim/nine_patch_layer.h"
#include "cc/slim/solid_color_layer.h"
#include "cc/slim/ui_resource_layer.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/android/resources/nine_patch_resource.h"
#include "ui/android/resources/resource_manager.h"
#include "ui/base/l10n/l10n_util_android.h"
#include "ui/gfx/color_utils.h"

namespace {

const SkColor kSearchBackgroundColor = SkColorSetRGB(0xee, 0xee, 0xee);
const SkColor kTouchHighlightColor = SkColorSetARGB(0x33, 0x99, 0x99, 0x99);

}  // namespace

namespace android {

// static
scoped_refptr<ContextualSearchLayer> ContextualSearchLayer::Create(
    ui::ResourceManager* resource_manager) {
  return base::WrapRefCounted(new ContextualSearchLayer(resource_manager));
}

void ContextualSearchLayer::SetProperties(
    int panel_shadow_resource_id,
    int search_bar_background_color,
    int search_context_resource_id,
    int search_term_resource_id,
    int search_caption_resource_id,
    int search_bar_shadow_resource_id,
    int search_provider_icon_resource_id,
    int quick_action_icon_resource_id,
    int drag_handlebar_resource_id,
    int open_tab_icon_resource_id,
    int close_icon_resource_id,
    int progress_bar_background_resource_id,
    int progress_bar_background_tint,
    int progress_bar_resource_id,
    int progress_bar_tint,
    int search_promo_resource_id,
    float dp_to_px,
    const scoped_refptr<cc::slim::Layer>& content_layer,
    bool search_promo_visible,
    float search_promo_height,
    float search_promo_opacity,
    int search_promo_background_color,
    // Related Searches
    int related_searches_in_bar_resource_id,
    bool related_searches_in_bar_visible,
    float related_searches_in_bar_height,
    float related_searches_in_bar_redundant_padding,
    // Position etc
    float search_panel_x,
    float search_panel_y,
    float search_panel_width,
    float search_panel_height,
    float search_bar_margin_side,
    float search_bar_margin_top,
    float search_bar_margin_bottom,
    float search_bar_height,
    float search_context_opacity,
    float search_text_layer_min_height,
    float search_term_opacity,
    float search_term_caption_spacing,
    float search_caption_animation_percentage,
    bool search_caption_visible,
    bool search_bar_border_visible,
    float search_bar_border_height,
    bool quick_action_icon_visible,
    bool thumbnail_visible,
    float custom_image_visibility_percentage,
    int bar_image_size,
    int icon_color,
    int drag_handlebar_color,
    float close_icon_opacity,
    bool progress_bar_visible,
    float progress_bar_height,
    float progress_bar_opacity,
    float progress_bar_completion,
    bool touch_highlight_visible,
    float touch_highlight_x_offset,
    float touch_highlight_width,
    int rounded_bar_top_resource_id,
    int separator_line_color,
    int callout_resource_id,
    float callout_opacity) {
  // Round values to avoid pixel gap between layers.
  search_bar_height = floor(search_bar_height);

  float search_bar_top = 0.f;
  float search_bar_bottom = search_bar_top + search_bar_height;
  bool should_render_progress_bar =
      progress_bar_visible && progress_bar_opacity > 0.f;

  OverlayPanelLayer::SetResourceIds(
      search_term_resource_id, panel_shadow_resource_id,
      rounded_bar_top_resource_id, search_bar_shadow_resource_id,
      search_provider_icon_resource_id, drag_handlebar_resource_id,
      open_tab_icon_resource_id, close_icon_resource_id);

  //  TODO(donnd): Update when moving Related Searches.
  float content_view_top = search_bar_bottom + search_promo_height;
  float should_render_bar_border = search_bar_border_visible
      && !should_render_progress_bar;

  // -----------------------------------------------------------------
  // Overlay Panel
  // -----------------------------------------------------------------
  OverlayPanelLayer::SetProperties(
      dp_to_px, content_layer, content_view_top, search_panel_x, search_panel_y,
      search_panel_width, search_panel_height, search_bar_background_color,
      search_bar_margin_side, search_bar_margin_top, search_bar_margin_bottom,
      search_bar_height, search_bar_top, search_term_opacity,
      should_render_bar_border, search_bar_border_height, icon_color,
      drag_handlebar_color, close_icon_opacity, separator_line_color,
      related_searches_in_bar_height);

  // -----------------------------------------------------------------
  // Content setup, to center in space below drag handle.
  // -----------------------------------------------------------------
  int content_height = search_bar_height - search_bar_margin_top -
                       related_searches_in_bar_height -
                       search_bar_margin_bottom;
  int content_top = search_bar_top + search_bar_margin_top;

  // ---------------------------------------------------------------------------
  // Search Term, Context and Search Caption
  // ---------------------------------------------------------------------------
  int text_layer_height =
      SetupTextLayer(content_top, content_height, search_text_layer_min_height,
                     search_caption_resource_id, search_caption_visible,
                     search_caption_animation_percentage, search_term_opacity,
                     search_context_resource_id, search_context_opacity,
                     search_term_caption_spacing);

  // Tracks the top of the next section to draw.
  int next_section_top = search_bar_bottom;

  // ---------------------------------------------------------------------------
  // Callout Control
  // ---------------------------------------------------------------------------
  if (callout_opacity > 0) {
    ui::Resource* callout_resource = resource_manager_->GetResource(
        ui::ANDROID_RESOURCE_TYPE_DYNAMIC, callout_resource_id);
    if (callout_resource) {
      if (callout_layer_->parent() != layer_) {
        layer_->AddChild(callout_layer_);
      }

      float callout_position_top = content_top + content_height / 2 -
                                   callout_resource->size().height() / 2;
      callout_layer_->SetUIResourceId(callout_resource->ui_resource()->id());
      callout_layer_->SetBounds(callout_resource->size());
      callout_layer_->SetPosition(gfx::PointF(0.f, callout_position_top));
      callout_layer_->SetOpacity(callout_opacity);
    }
  } else if (callout_layer_->parent()) {
    callout_layer_->RemoveFromParent();
  }

  // ---------------------------------------------------------------------------
  // Related Searches In-Bar Control
  // ---------------------------------------------------------------------------
  if (related_searches_in_bar_visible) {
    // Grabs the Related Searches in-bar resource.
    ui::Resource* related_searches_resource = resource_manager_->GetResource(
        ui::ANDROID_RESOURCE_TYPE_DYNAMIC, related_searches_in_bar_resource_id);
    DCHECK(related_searches_resource);
    if (related_searches_resource) {
      gfx::Size related_searches_size(
          search_panel_width, related_searches_resource->size().height());
      if (related_searches_in_bar_->parent() != layer_) {
        layer_->AddChild(related_searches_in_bar_);
      }
      related_searches_in_bar_->SetUIResourceId(
          related_searches_resource->ui_resource()->id());
      related_searches_in_bar_->SetBounds(related_searches_size);
      int related_searches_top =
          search_bar_bottom - related_searches_in_bar_height -
          related_searches_in_bar_redundant_padding - search_bar_margin_bottom;
      related_searches_in_bar_->SetPosition(
          gfx::PointF(0.f, related_searches_top));
    }
  } else if (related_searches_in_bar_.get() &&
             related_searches_in_bar_->parent()) {
    related_searches_in_bar_->RemoveFromParent();
  }

  // ---------------------------------------------------------------------------
  // Search Promo
  // ---------------------------------------------------------------------------
  if (search_promo_visible) {
    // Grabs the Search Opt Out Promo resource.
    ui::Resource* search_promo_resource = resource_manager_->GetResource(
        ui::ANDROID_RESOURCE_TYPE_DYNAMIC, search_promo_resource_id);
    // Search Promo Container
    if (search_promo_container_->parent() != layer_) {
      // NOTE(donnd): This layer can appear just below the Bar so it should be
      // always placed before the Search Bar Shadow to make sure it won't
      // occlude the shadow. Since layer 0 is the shadow for the sheet itself,
      // this needs to be layer 1.
      layer_->InsertChild(search_promo_container_, 1);
    }

    if (search_promo_resource) {
      int search_promo_content_height = search_promo_resource->size().height();
      gfx::Size search_promo_size(search_panel_width, search_promo_height);
      search_promo_container_->SetBounds(search_promo_size);
      search_promo_container_->SetPosition(gfx::PointF(0.f, next_section_top));
      search_promo_container_->SetMasksToBounds(true);
      // TODO(crbug.com/40219248): Remove FromColor and make all SkColor4f.
      search_promo_container_->SetBackgroundColor(
          SkColor4f::FromColor(search_promo_background_color));

      // Search Promo
      if (search_promo_->parent() != search_promo_container_)
        search_promo_container_->AddChild(search_promo_);

      search_promo_->SetUIResourceId(
          search_promo_resource->ui_resource()->id());
      search_promo_->SetBounds(search_promo_resource->size());
      // Align promo at the bottom of the container so the confirmation button
      // is not clipped when resizing the promo.
      search_promo_->SetPosition(
          gfx::PointF(0.f, search_promo_height - search_promo_content_height));
      search_promo_->SetOpacity(search_promo_opacity);
      // Next section goes beyond this section.
      next_section_top += search_promo_content_height;
    }
  } else {
    // Search Promo Container
    if (search_promo_container_.get() && search_promo_container_->parent())
      search_promo_container_->RemoveFromParent();
  }

  // ---------------------------------------------------------------------------
  // Progress Bar
  // ---------------------------------------------------------------------------
  OverlayPanelLayer::SetProgressBar(
      progress_bar_background_resource_id, progress_bar_background_tint,
      progress_bar_resource_id, progress_bar_tint, progress_bar_visible,
      search_bar_bottom, progress_bar_height, progress_bar_opacity,
      progress_bar_completion, search_panel_width);

  // ---------------------------------------------------------------------------
  // Touch Highlight Layer
  // ---------------------------------------------------------------------------
  if (touch_highlight_visible) {
    if (touch_highlight_layer_->parent() != layer_)
      layer_->AddChild(touch_highlight_layer_);
    // In the new layout don't highlight the whole bar due to rounded corners.
    int highlight_height = text_layer_height;
    int highlight_top = content_top;
    highlight_top += (content_height - text_layer_height) / 2;
    gfx::Size background_size(touch_highlight_width, highlight_height);
    touch_highlight_layer_->SetBounds(background_size);
    touch_highlight_layer_->SetPosition(
        gfx::PointF(touch_highlight_x_offset, highlight_top));
  } else {
    touch_highlight_layer_->RemoveFromParent();
  }

  // ---------------------------------------------------------------------------
  // Icon Layer
  // ---------------------------------------------------------------------------
  bar_image_size_ = bar_image_size;
  SetupIconLayer(search_provider_icon_resource_id, quick_action_icon_visible,
                 quick_action_icon_resource_id, thumbnail_visible,
                 custom_image_visibility_percentage);
}

scoped_refptr<cc::slim::Layer> ContextualSearchLayer::GetIconLayer() {
  return icon_layer_;
}

void ContextualSearchLayer::SetupIconLayer(
    int search_provider_icon_resource_id,
    bool quick_action_icon_visible,
    int quick_action_icon_resource_id,
    bool thumbnail_visible,
    float custom_image_visibility_percentage) {
  icon_layer_->SetBounds(gfx::Size(bar_image_size_, bar_image_size_));
  icon_layer_->SetMasksToBounds(true);

  if (quick_action_icon_visible) {
    if (quick_action_icon_layer_->parent() != icon_layer_)
      icon_layer_->AddChild(quick_action_icon_layer_);

    ui::Resource* quick_action_icon_resource = resource_manager_->GetResource(
        ui::ANDROID_RESOURCE_TYPE_DYNAMIC, quick_action_icon_resource_id);
    if (quick_action_icon_resource) {
      quick_action_icon_layer_->SetUIResourceId(
          quick_action_icon_resource->ui_resource()->id());
      quick_action_icon_layer_->SetBounds(
          gfx::Size(bar_image_size_, bar_image_size_));

      SetCustomImageProperties(quick_action_icon_layer_, 0, 0,
                               custom_image_visibility_percentage);
    }
  } else if (quick_action_icon_layer_->parent()) {
    quick_action_icon_layer_->RemoveFromParent();
  }

  // Thumbnail
  if (!quick_action_icon_visible && thumbnail_visible) {
    if (thumbnail_layer_->parent() != icon_layer_)
          icon_layer_->AddChild(thumbnail_layer_);

    SetCustomImageProperties(thumbnail_layer_, thumbnail_top_margin_,
                             thumbnail_side_margin_,
                             custom_image_visibility_percentage);
  } else if (thumbnail_layer_->parent()) {
    thumbnail_layer_->RemoveFromParent();
  }

  // Search Provider Icon
  if (search_provider_icon_layer_->parent() != icon_layer_)
    icon_layer_->AddChild(search_provider_icon_layer_);

  ui::Resource* search_provider_icon_resource = resource_manager_->GetResource(
      ui::ANDROID_RESOURCE_TYPE_STATIC, search_provider_icon_resource_id);
  if (search_provider_icon_resource) {
    gfx::Size icon_size = search_provider_icon_resource->size();
    search_provider_icon_layer_->SetUIResourceId(
        search_provider_icon_resource->ui_resource()->id());
    search_provider_icon_layer_->SetBounds(icon_size);

    search_provider_icon_layer_->SetOpacity(1.f -
                                            custom_image_visibility_percentage);

    // Determine x and y offsets to center the icon in its parent layer
    float icon_x_offset = (bar_image_size_ - icon_size.width()) / 2;
    float icon_y_offset = (bar_image_size_ - icon_size.height()) / 2;

    // Determine extra y-offset if thumbnail or quick action are visible.
    icon_y_offset -= (bar_image_size_ * custom_image_visibility_percentage);

    search_provider_icon_layer_->SetPosition(
        gfx::PointF(icon_x_offset, icon_y_offset));
  }
}

void ContextualSearchLayer::SetCustomImageProperties(
    scoped_refptr<cc::slim::UIResourceLayer> custom_image_layer,
    float top_margin,
    float side_margin,
    float visibility_percentage) {
  custom_image_layer->SetOpacity(visibility_percentage);

  // When animating, the custom image and search provider icon slide through
  // |icon_layer_|. This effect is achieved by changing the y-offset
  // for each child layer.
  // If the custom image has a height less than |bar_image_size_|, it will
  // have a top margin that needs to be accounted for while running the
  // animation. The final |custom_image_y_offset| should be equal to
  // |tpp_margin|.
  float custom_image_y_offset =
      (bar_image_size_ * (1.f - visibility_percentage)) + top_margin;
  custom_image_layer->SetPosition(
      gfx::PointF(side_margin, custom_image_y_offset));
}

int ContextualSearchLayer::SetupTextLayer(float content_top,
                                          float content_height,
                                          float search_text_layer_min_height,
                                          int caption_resource_id,
                                          bool caption_visible,
                                          float animation_percentage,
                                          float search_term_opacity,
                                          int context_resource_id,
                                          float context_opacity,
                                          float term_caption_spacing) {
  // ---------------------------------------------------------------------------
  // Setup the Drawing Hierarchy
  // ---------------------------------------------------------------------------
  // Search Term
  DCHECK(text_layer_.get());
  DCHECK(bar_text_.get());
  DCHECK(search_caption_.get());
  bool bar_text_visible = search_term_opacity > 0.0f;
  if (bar_text_visible && bar_text_->parent() != text_layer_)
    text_layer_->AddChild(bar_text_);

  // Search Context
  ui::Resource* context_resource = resource_manager_->GetResource(
      ui::ANDROID_RESOURCE_TYPE_DYNAMIC, context_resource_id);
  if (context_resource) {
    search_context_->SetUIResourceId(context_resource->ui_resource()->id());
    search_context_->SetBounds(context_resource->size());
  }

  // Search Caption
  ui::Resource* caption_resource = nullptr;
  if (caption_visible) {
    // Grabs the dynamic Search Caption resource so we can get a snapshot.
    caption_resource  = resource_manager_->GetResource(
        ui::ANDROID_RESOURCE_TYPE_DYNAMIC, caption_resource_id);
  }

  if (caption_visible && animation_percentage != 0.f) {
    if (search_caption_->parent() != text_layer_) {
      text_layer_->AddChild(search_caption_);
    }
    if (caption_resource) {
      search_caption_->SetUIResourceId(caption_resource->ui_resource()->id());
      search_caption_->SetBounds(caption_resource->size());
    }
  } else if (search_caption_->parent()) {
    search_caption_->RemoveFromParent();
  }

  // ---------------------------------------------------------------------------
  // Calculate Text Layer Size
  // ---------------------------------------------------------------------------
  // If space allows, the Term, Context and Caption should occupy a Text
  // section of fixed size.
  // We may not be able to fit these inside the ideal size as the user may have
  // their Font Size set to large.

  // The Term might not be visible or initialized yet, so set up main_text with
  // whichever main bar text seems appropriate.
  scoped_refptr<cc::slim::UIResourceLayer> main_text =
      (bar_text_visible ? bar_text_ : search_context_);

  // The search_caption_ may not have had it's resource set by this point, if so
  // the bounds will be zero and everything will still work.
  float term_height = main_text->bounds().height();
  float caption_height = search_caption_->bounds().height();

  float layer_height = std::max(search_text_layer_min_height,
      term_height + caption_height + term_caption_spacing);
  float layer_width =
      std::max(main_text->bounds().width(), search_caption_->bounds().width());

  float layer_top = content_top + (content_height - layer_height) / 2;
  text_layer_->SetBounds(gfx::Size(layer_width, layer_height));
  text_layer_->SetPosition(gfx::PointF(0.f, layer_top));
  text_layer_->SetMasksToBounds(true);

  // ---------------------------------------------------------------------------
  // Layout Text Layer
  // ---------------------------------------------------------------------------
  // ---Top of Search Bar--- <- bar_top
  //
  // ---Top of Text Layer--- <- layer_top
  //                         } remaining_height / 2
  // Term & Context          } term_height
  //                         } term_caption_spacing
  // Caption                 } caption_height
  //                         } remaining_height / 2
  // --Bottom of Text Layer-
  //
  // --Bottom of Search Bar-
  // If the Caption is not visible the Term is centered in this space, when
  // the Caption becomes visible it is animated sliding up into it's position
  // with the spacings determined by UI.
  // The Term and the Context are assumed to be the same height and will be
  // positioned one on top of the other. When the Context is resolved it will
  // fade out and the Term will fade in.

  search_context_->SetOpacity(context_opacity);
  bar_text_->SetOpacity(search_term_opacity);

  // If there is no caption, just vertically center the Search Term.
  float term_top = (layer_height - term_height) / 2;

  // If we aren't displaying the caption we're done.
  if (!caption_visible || animation_percentage == 0.f || !caption_resource) {
    bar_text_->SetPosition(gfx::PointF(0.f, term_top));
    search_context_->SetPosition(gfx::PointF(0.f, term_top));
    return layer_height;
  }

  // Calculate the positions for the Term and Caption when the Caption
  // animation is complete.
  float remaining_height = layer_height
                         - term_height
                         - term_caption_spacing
                         - caption_height;

  float term_top_end = remaining_height / 2;
  float caption_top_end = term_top_end + term_height + term_caption_spacing;

  // Interpolate between the animation start and end positions (short cut
  // if the animation is at the end or start).
  term_top = term_top * (1.f - animation_percentage)
           + term_top_end * animation_percentage;

  // The Caption starts off the bottom of the Text Layer.
  float caption_top = layer_height * (1.f - animation_percentage)
                    + caption_top_end * animation_percentage;

  bar_text_->SetPosition(gfx::PointF(0.f, term_top));
  search_context_->SetPosition(gfx::PointF(0.f, term_top));
  search_caption_->SetPosition(gfx::PointF(0.f, caption_top));
  return layer_height;
}

void ContextualSearchLayer::SetThumbnail(const SkBitmap* thumbnail) {
  // Determine the scaled thumbnail width and height. If both the height and
  // width of |thumbnail| are larger than |bar_image_size_|, the thumbnail
  // will be scaled down by a call to Layer::SetBounds() below.
  int min_dimension = std::min(thumbnail->width(), thumbnail->height());
  int scaled_thumbnail_width = thumbnail->width();
  int scaled_thumbnail_height = thumbnail->height();
  if (min_dimension > bar_image_size_) {
    scaled_thumbnail_width =
        scaled_thumbnail_width * bar_image_size_ / min_dimension;
    scaled_thumbnail_height =
        scaled_thumbnail_height * bar_image_size_ / min_dimension;
  }

  // Determine the UV transform coordinates. This will crop the thumbnail.
  // (0, 0) is the default top left corner. (1, 1) is the default bottom
  // right corner.
  float top_left_x = 0;
  float top_left_y = 0;
  float bottom_right_x = 1;
  float bottom_right_y = 1;

  if (scaled_thumbnail_width > bar_image_size_) {
    // Crop an even amount on the left and right sides of the thumbnail.
    float top_left_x_px = (scaled_thumbnail_width - bar_image_size_) / 2.f;
    float bottom_right_x_px = top_left_x_px + bar_image_size_;

    top_left_x = top_left_x_px / scaled_thumbnail_width;
    bottom_right_x = bottom_right_x_px / scaled_thumbnail_width;
  } else if (scaled_thumbnail_height > bar_image_size_) {
    // Crop an even amount on the top and bottom of the thumbnail.
    float top_left_y_px = (scaled_thumbnail_height - bar_image_size_) / 2.f;
    float bottom_right_y_px = top_left_y_px + bar_image_size_;

    top_left_y = top_left_y_px / scaled_thumbnail_height;
    bottom_right_y = bottom_right_y_px / scaled_thumbnail_height;
  }

  // If the original |thumbnail| height or width is smaller than
  // |bar_image_size_| determine the side and top margins needed to center
  // the thumbnail.
  thumbnail_side_margin_ = 0;
  thumbnail_top_margin_ = 0;

  if (scaled_thumbnail_width < bar_image_size_) {
    thumbnail_side_margin_ = (bar_image_size_ - scaled_thumbnail_width) / 2.f;
  }

  if (scaled_thumbnail_height < bar_image_size_) {
    thumbnail_top_margin_ = (bar_image_size_ - scaled_thumbnail_height) / 2.f;
  }

  // Determine the layer bounds. This will down scale the thumbnail if
  // necessary and ensure it is displayed at |bar_image_size_|. If
  // either the original |thumbnail| height or width is smaller than
  // |bar_image_size_|, the thumbnail will not be scaled.
  int layer_width = std::min(bar_image_size_, scaled_thumbnail_width);
  int layer_height = std::min(bar_image_size_, scaled_thumbnail_height);

  // UIResourceLayer requires an immutable copy of the input |thumbnail|.
  SkBitmap thumbnail_copy;
  if (thumbnail->isImmutable()) {
    thumbnail_copy = *thumbnail;
  } else {
    if (thumbnail_copy.tryAllocPixels(thumbnail->info())) {
      thumbnail->readPixels(thumbnail_copy.info(), thumbnail_copy.getPixels(),
                            thumbnail_copy.rowBytes(), 0, 0);
    }
    thumbnail_copy.setImmutable();
  }

  thumbnail_layer_->SetBitmap(thumbnail_copy);
  thumbnail_layer_->SetBounds(gfx::Size(layer_width, layer_height));
  thumbnail_layer_->SetPosition(
      gfx::PointF(thumbnail_side_margin_, thumbnail_top_margin_));
  thumbnail_layer_->SetUV(gfx::PointF(top_left_x, top_left_y),
                          gfx::PointF(bottom_right_x, bottom_right_y));
}

ContextualSearchLayer::ContextualSearchLayer(
    ui::ResourceManager* resource_manager)
    : OverlayPanelLayer(resource_manager),
      search_context_(cc::slim::UIResourceLayer::Create()),
      icon_layer_(cc::slim::Layer::Create()),
      search_provider_icon_layer_(cc::slim::UIResourceLayer::Create()),
      thumbnail_layer_(cc::slim::UIResourceLayer::Create()),
      quick_action_icon_layer_(cc::slim::UIResourceLayer::Create()),
      search_promo_(cc::slim::UIResourceLayer::Create()),
      search_promo_container_(cc::slim::SolidColorLayer::Create()),
      related_searches_in_bar_(cc::slim::UIResourceLayer::Create()),
      search_caption_(cc::slim::UIResourceLayer::Create()),
      text_layer_(cc::slim::UIResourceLayer::Create()),
      touch_highlight_layer_(cc::slim::SolidColorLayer::Create()),
      callout_layer_(cc::slim::UIResourceLayer::Create()) {
  // Search Bar Text
  search_context_->SetIsDrawable(true);

  // Search Bar Caption
  search_caption_->SetIsDrawable(true);

  // Search Opt Out Promo
  search_promo_container_->SetIsDrawable(true);
  search_promo_container_->SetBackgroundColor(
      SkColor4f::FromColor(kSearchBackgroundColor));
  search_promo_->SetIsDrawable(true);

  // Related Searches sections
  related_searches_in_bar_->SetIsDrawable(true);

  // Icon - holds thumbnail, search provider icon and/or quick action icon
  icon_layer_->SetIsDrawable(true);
  layer_->AddChild(icon_layer_);

  // Search provider icon
  search_provider_icon_layer_->SetIsDrawable(true);

  // Thumbnail
  thumbnail_layer_->SetIsDrawable(true);

  // Quick action icon
  quick_action_icon_layer_->SetIsDrawable(true);

  // Content layer
  text_layer_->SetIsDrawable(true);
  // NOTE(mdjones): This can be called multiple times to add other text layers.
  AddBarTextLayer(text_layer_);
  text_layer_->AddChild(search_context_);

  // Touch Highlight Layer
  touch_highlight_layer_->SetIsDrawable(true);
  touch_highlight_layer_->SetBackgroundColor(
      SkColor4f::FromColor(kTouchHighlightColor));

  // Callout Layer
  callout_layer_->SetIsDrawable(true);
}

ContextualSearchLayer::~ContextualSearchLayer() = default;

}  //  namespace android