File: bubble_dialog_delegate_view.h

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 (951 lines) | stat: -rw-r--r-- 38,276 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
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef UI_VIEWS_BUBBLE_BUBBLE_DIALOG_DELEGATE_VIEW_H_
#define UI_VIEWS_BUBBLE_BUBBLE_DIALOG_DELEGATE_VIEW_H_

#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <unordered_set>
#include <utility>

#include "base/check.h"
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/raw_span.h"
#include "base/memory/weak_ptr.h"
#include "base/types/pass_key.h"
#include "build/build_config.h"
#include "ui/accessibility/ax_enums.mojom-forward.h"
#include "ui/base/class_property.h"
#include "ui/base/metadata/metadata_header_macros.h"
#include "ui/base/metadata/metadata_utils.h"
#include "ui/base/mojom/dialog_button.mojom.h"
#include "ui/base/mojom/ui_base_types.mojom-shared.h"
#include "ui/color/color_variant.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/layer_type.h"
#include "ui/views/bubble/bubble_border.h"
#include "ui/views/bubble/bubble_frame_view.h"
#include "ui/views/metadata/view_factory.h"
#include "ui/views/view_tracker.h"
#include "ui/views/view_utils.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_observer.h"
#include "ui/views/window/dialog_delegate.h"

#if BUILDFLAG(IS_MAC)
#include "ui/base/cocoa/bubble_closer.h"
#endif

class AccountChooserDialogView;
class AppDialogView;
class AnnouncementView;
class BruschettaUninstallerView;
class ChromeLabsBubbleView;
class ColorPickerViewTest;
class ContentSettingBubbleContents;
class CriticalNotificationBubbleView;
class CrostiniAnsibleSoftwareConfigView;
class CrostiniExpiredContainerWarningView;
class CrostiniForceCloseView;
class CrostiniPackageInstallFailureView;
class CrostiniRecoveryView;
class CrostiniUninstallerView;
class CrostiniUpdateFilesystemView;
class DiceWebSigninInterceptionBubbleView;
class ExtensionInstallDialogView;
class ExtensionInstallFrictionDialogView;
class ExtensionInstalledBubbleView;
class ExtensionPopup;
class ExtensionsMenuView;
class FlyingIndicator;
class GlobalErrorBubbleView;
class HomePageUndoBubble;
class MediaDialogView;
class HatsNextWebDialog;
class IncognitoClearBrowsingDataDialog;
class LocationBarBubbleDelegateView;
class NetworkProfileBubbleView;
class PageInfoBubbleViewBase;
class PermissionPromptBaseView;
class PluginVmInstallerView;
class ProfileCustomizationBubbleView;
class ProfileMenuViewBase;
class RemoveSuggestionBubbleDialogDelegateView;
class StoragePressureBubbleView;
class TabGroupEditorBubbleView;
class TabHoverCardBubbleView;
class TestBubbleView;
class ToolbarActionHoverCardBubbleView;
class ToolbarActionsBarBubbleViews;
class ScreenshotSurfaceTestDialog;
class WebBubbleView;
class WebUIBubbleDialogView;
FORWARD_DECLARE_TEST(InProcessBrowserTest,
                     RunsScheduledLayoutOnAnchoredBubbles);

namespace ambient_signin {
class AmbientSigninBubbleView;
}

namespace arc {
class ArcSplashScreenDialogView;
class BaseDialogDelegateView;
class ResizeConfirmationDialogView;
class RoundedCornerBubbleDialogDelegateView;

namespace input_overlay {
class DeleteEditShortcut;
class RichNudge;
}  // namespace input_overlay
}  // namespace arc

namespace ash {
class AnchoredNudge;
class ContextualNudge;
class DictationBubbleView;
class FaceGazeBubbleView;
class GameDashboardMainMenuView;
class HelpBubbleViewAsh;
class ImeModeIndicatorView;
class KioskAppInstructionBubble;
class MouseKeysBubbleView;
class NetworkInfoBubble;
class NetworkStateListInfoBubble;
class PaletteWelcomeBubbleView;
class QuickInsertCapsLockStateView;
class QuickInsertPreviewBubbleView;
class ShelfBubble;
class TestBubbleDialogDelegateView;
class TestBubbleDialogDelegate;
class TrayBubbleView;
FORWARD_DECLARE_TEST(OverviewSessionTest, DoNotHideBubbleTransient);
FORWARD_DECLARE_TEST(ResizeShadowAndCursorTest,
                     DefaultCursorOnBubbleWidgetCorners);
FORWARD_DECLARE_TEST(SnapGroupOverviewTest, BubbleTransientIsVisibleInOverview);
FORWARD_DECLARE_TEST(
    SnapGroupDesksTest,
    NoCrashWhenDraggingOverviewGroupItemWithBubbleToAnotherDesk);
FORWARD_DECLARE_TEST(SnapGroupTest,
                     NoCrashWhenReSnappingSecondaryToPrimaryWithTransient);

namespace sharesheet {
class SharesheetBubbleView;
}
}  // namespace ash

namespace autofill {
class CardUnmaskAuthenticationSelectionDialogView;
class CardUnmaskPromptViews;
class LocalCardMigrationDialogView;
class LocalCardMigrationErrorDialogView;
}  // namespace autofill

namespace captions {
class CaptionBubble;
}

namespace chromeos {
class MultitaskMenu;
}

namespace gfx {
class Rect;
}

namespace lens {
class LensPreselectionBubble;
class LensRegionSearchInstructionsView;
}  // namespace lens

namespace media_router {
class CastDialogView;
class MediaRemotingDialogView;
}  // namespace media_router

namespace send_tab_to_self {
class SendTabToSelfToolbarBubbleView;
}

namespace toasts {
class ToastView;
}

namespace ui::ime {
class AnnouncementView;
class CandidateWindowView;
class GrammarSuggestionWindow;
class InfolistWindow;
class SuggestionWindowView;
class UndoWindow;
}  // namespace ui::ime

namespace user_education {
class HelpBubbleView;

namespace test {
class TestCustomHelpBubbleView;
}
}  // namespace user_education

namespace webid {
class AccountSelectionBubbleView;
}

namespace views {

class AnchorTestBubbleDialogDelegateView;
class Button;
class FocusManagerTestBubbleDialogDelegateView;
class FrameViewTestBubbleDialogDelegateView;
class InfoBubble;
class InteractionSequenceViewsTest;
class TestBubbleDialogDelegateView;
class TestBubbleView;
class TouchSelectionMenuViews;

namespace examples {
template <class DialogType>
class DialogExampleDelegate;
class ExampleBubble;
class LoginBubbleDialogView;
}  // namespace examples

namespace test {
class SimpleBubbleView;
class TestBubbleView;
class WidgetTestBubbleDialogDelegateView;
FORWARD_DECLARE_TEST(DesktopWidgetTestInteractive, FocusChangesOnBubble);
FORWARD_DECLARE_TEST(InteractionTestUtilViewsTest, ActivateSurface);
FORWARD_DECLARE_TEST(InteractionTestUtilViewsTest, Confirm);
}  // namespace test

class VIEWS_EXPORT BubbleDialogDelegate : public DialogDelegate {
 public:
  BubbleDialogDelegate(
      View* anchor_view,
      BubbleBorder::Arrow arrow,
      BubbleBorder::Shadow shadow = BubbleBorder::DIALOG_SHADOW,
      bool autosize = false);
  BubbleDialogDelegate(const BubbleDialogDelegate& other) = delete;
  BubbleDialogDelegate& operator=(const BubbleDialogDelegate& other) = delete;
  ~BubbleDialogDelegate() override;

  // DialogDelegate:
  BubbleDialogDelegate* AsBubbleDialogDelegate() override;
  std::unique_ptr<NonClientFrameView> CreateNonClientFrameView(
      Widget* widget) override;
  ClientView* CreateClientView(Widget* widget) override;
  ax::mojom::Role GetAccessibleWindowRole() final;

  // Create and initialize the bubble Widget with proper bounds.
  // The default ownership for now is NATIVE_WIDGET_OWNS_WIDGET. If any other
  // ownership mode is used, the returned Widget's lifetime must be managed by
  // the caller. This is usually done by wrapping the pointer as a unique_ptr
  // using base::WrapUnique().
  static Widget* CreateBubble(
      std::unique_ptr<BubbleDialogDelegate> bubble_delegate,
      Widget::InitParams::Ownership ownership =
          Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET);

  //////////////////////////////////////////////////////////////////////////////
  // The anchor view and rectangle:
  //
  // The anchor view takes priority over the anchor rectangle.
  // If the anchor moves, BubbleDialogDelegate will move its Widget to maintain
  // the same position relative to its anchor. If an anchor view is used this
  // happens automatically; if an anchor rect is used, the new anchor rect needs
  // to be supplied via SetAnchorRect().

  void SetAnchorView(View* view);
  View* GetAnchorView() const;

  void SetMainImage(ui::ImageModel main_image);
  const ui::ImageModel& GetMainImage() const { return main_image_; }

  // GetAnchorRect() takes into account the presence of an anchor view, while
  // anchor_rect() always returns the configured anchor rect, regardless of
  // whether there is also an anchor view. While it is possible to override
  // GetAnchorRect(), you should not need to do so; if you do, you must remember
  // to call OnAnchorBoundsChanged() when the return value of GetAnchorRect()
  // changes.
  //
  // TODO(ellyjones): Remove overrides of GetAnchorRect() and make this not
  // virtual.
  virtual gfx::Rect GetAnchorRect() const;
  const std::optional<gfx::Rect>& anchor_rect() const { return anchor_rect_; }
  void SetAnchorRect(const gfx::Rect& rect);

  //////////////////////////////////////////////////////////////////////////////
  // The anchor widget:
  //
  // The bubble will close when the anchor widget closes. Also, when the anchor
  // widget moves, the bubble will recompute its location from its anchor view.
  // The bubble will also cause its anchor widget to paint as active when the
  // bubble is active, and will optionally resize itself to fit within the
  // anchor widget if the anchor widget's size changes.
  //
  // The anchor widget can be explicitly set, or is implied by the anchor view.
  void SetAnchorWidget(views::Widget* anchor_widget);
  Widget* anchor_widget() { return anchor_widget_; }
  const Widget* anchor_widget() const { return anchor_widget_; }

  //////////////////////////////////////////////////////////////////////////////
  // The arrow:
  //
  // Each bubble has an "arrow", which describes the relationship between the
  // bubble's position and the position of its anchor view. The arrow also
  // supplies the - anchor offset eg, a top-left arrow puts the bubble below and
  // to the right of the anchor view, and so on. The "arrow" name is a holdover
  // from an earlier time when the arrow was an actual visual marker on the
  // bubble's border as well, but these days the arrow has no visual presence.
  //
  // The arrow is automatically flipped in RTL locales, and by default is
  // manually adjusted if necessary to fit the bubble on screen.

  // Sets the desired arrow for the bubble and updates the bubble's bounds.
  void SetArrow(BubbleBorder::Arrow arrow);
  BubbleBorder::Arrow arrow() const { return arrow_; }

  // Sets the arrow without recalculating or updating bounds. This could be used
  // before another function call which also sets bounds, so that bounds are
  // not set multiple times in a row. When animating bounds changes, setting
  // bounds twice in a row can make the widget position jump.
  // TODO(crbug.com/41470150) It would be good to be able to re-target the
  // animation rather than expect callers to use SetArrowWithoutResizing if they
  // are also changing the anchor rect, or similar.
  void SetArrowWithoutResizing(BubbleBorder::Arrow arrow);

  // Whether the arrow will be automatically adjusted if needed to fit the
  // bubble on screen. Has no effect if the bubble has no arrow.
  bool adjust_if_offscreen() const { return adjust_if_offscreen_; }
  void set_adjust_if_offscreen(bool adjust) { adjust_if_offscreen_ = adjust; }

  //////////////////////////////////////////////////////////////////////////////
  // Shadows:
  //
  // Bubbles may optionally have a shadow. Only some platforms support drawing
  // custom shadows on a bubble.

  BubbleBorder::Shadow GetShadow() const;
  void set_shadow(BubbleBorder::Shadow shadow) { shadow_ = shadow; }

  // Call this method to inform BubbleDialogDelegate that the return value of
  // GetAnchorRect() has changed. You only need to do this if you have
  // overridden GetAnchorRect() - if you are using an anchor view or anchor rect
  // normally, do not call this.
  virtual void OnAnchorBoundsChanged();

  // Call this method to update view shown time stamp of underneath input
  // protectors.
  void UpdateInputProtectorsTimeStamp();

  //////////////////////////////////////////////////////////////////////////////
  // Subtitle:
  //
  // Bubbles have an optional a Subtitle label under the Title.
  // This subtitle label is represented in BubbleFrameView.

  // This method is virtual for BubbleFrameViewUnitTest purposes.
  // Not intended to be overridden in production.
  virtual std::u16string GetSubtitle() const;
  void SetSubtitle(const std::u16string& subtitle);

  bool GetSubtitleAllowCharacterBreak() const;
  void SetSubtitleAllowCharacterBreak(bool allow);

  // No setter: autosize_ should not be changed after construction.
  bool is_autosized() const { return autosize_; }

  //////////////////////////////////////////////////////////////////////////////
  // Miscellaneous bubble behaviors:
  //

  // Represents a pin that prevents a widget from closing on deactivation, even
  // if `close_on_deactivate` is set to true. Prevents closing on deactivation
  // until its destruction; if it outlives the widget it does nothing.
  class VIEWS_EXPORT CloseOnDeactivatePin {
   public:
    virtual ~CloseOnDeactivatePin();

    CloseOnDeactivatePin(const CloseOnDeactivatePin&) = delete;
    void operator=(const CloseOnDeactivatePin&) = delete;

   private:
    class Pins;
    friend class BubbleDialogDelegate;
    explicit CloseOnDeactivatePin(base::WeakPtr<Pins> pins);

    const base::WeakPtr<Pins> pins_;
  };

  // Whether the bubble closes when it ceases to be the active window.
  void set_close_on_deactivate(bool close) { close_on_deactivate_ = close; }

  // Returns whether the bubble should close on deactivation. May not match
  // `close_on_deactivate` if PreventCloseOnDeactivate() has been called.
  bool ShouldCloseOnDeactivate() const;

  // Prevents close-on-deactivate for the duration of the lifetime of the pin
  // that is returned. The pin does nothing after the widget is closed.
  std::unique_ptr<CloseOnDeactivatePin> PreventCloseOnDeactivate();

  // Explicitly set the button to automatically highlight when the bubble is
  // shown. By default the anchor is highlighted, if it is a button.
  //
  // TODO(ellyjones): Is there ever a situation where this is the right thing to
  // do UX-wise? It seems very odd to highlight something other than the anchor
  // view.
  void SetHighlightedButton(Button* highlighted_button);

  // The bubble's parent window - this can only be usefully set before creating
  // the bubble's widget. If there is one, the bubble will be stacked above it,
  // and it will become the Views parent window for the bubble.
  //
  // TODO(ellyjones):
  // - When does one actually need to call this?
  // - Why is it separate from the anchor widget?
  // - Why do most bubbles seem to work fine without this?
  gfx::NativeView parent_window() const { return parent_window_; }
  void set_parent_window(gfx::NativeView window) { parent_window_ = window; }

  bool has_parent() { return has_parent_; }
  void set_has_parent(bool has_parent) { has_parent_ = has_parent; }

  // Whether the bubble accepts mouse events or not.
  bool accept_events() const { return accept_events_; }
  void set_accept_events(bool accept_events) { accept_events_ = accept_events; }

  // Whether focus can traverse from the anchor view into the bubble. Only
  // meaningful if there is an anchor view.
  // TODO(pbos): See if this can be inferred from if the bubble is activatable
  // or if there's anything focusable within the dialog. This is currently used
  // for bubbles that should never receive focus and we should be able have
  // focus go through a bubble if nothing's focusable within it. Without this
  // set to `false`, the existence of an InfoBubble in the QR reader bubble will
  // break focus order in the parent dialog. This is a bug for which
  // set_focus_traversable_from_anchor_view(false) is used as a workaround. See
  // if fixing that bug removes the need for this for other dialogs.
  void set_focus_traversable_from_anchor_view(bool focusable) {
    focus_traversable_from_anchor_view_ = focusable;
  }

  // If this is true and either:
  // - The anchor View is a Button, or
  // - The highlighted Button is set,
  // then BubbleDialogDelegate will ask the anchor View / highlighted button to
  // highlight itself when the BubbleDialogDelegate's Widget is shown.
  void set_highlight_button_when_shown(bool highlight) {
    highlight_button_when_shown_ = highlight;
  }

  //////////////////////////////////////////////////////////////////////////////
  // Layout & colors:
  //
  // In general you shouldn't need to call any setters. If the default bubble
  // look and feel does not work for your use case, BubbleDialogDelegate may not
  // be a good fit for the UI you are building.

  ui::ColorVariant background_color() const { return color_; }
  void SetBackgroundColor(ui::ColorVariant color);

  void set_title_margins(const gfx::Insets& title_margins) {
    title_margins_ = title_margins;
  }

  gfx::Insets footnote_margins() const { return footnote_margins_; }
  void set_footnote_margins(const gfx::Insets& footnote_margins) {
    footnote_margins_ = footnote_margins;
  }

  // Sets the content margins to a default picked for smaller bubbles.
  void UseCompactMargins();

  // Set/Get the layer type of the bubble widget and client view.
  ui::LayerType layer_type() const { return layer_type_; }
  void set_layer_type(ui::LayerType layer_type) {
    CHECK(layer_type == ui::LAYER_TEXTURED ||
          layer_type == ui::LAYER_NOT_DRAWN);
    layer_type_ = layer_type;
  }

  // Override to provide custom parameters before widget initialization.
  virtual void OnBeforeBubbleWidgetInit(Widget::InitParams* params,
                                        Widget* widget) const {}

  // Get the maximum available screen space to place a bubble anchored to
  // |anchor_view| at |arrow|. If offscreen adjustment is on, this would return
  // the max space corresponding to the possible arrow positions of the bubble.
  // NOTE: This function should not be called in ozone platforms where global
  // screen coordinates are not available.
  static gfx::Size GetMaxAvailableScreenSpaceToPlaceBubble(
      View* anchor_view,
      BubbleBorder::Arrow arrow,
      bool adjust_if_offscreen,
      BubbleFrameView::PreferredArrowAdjustment arrow_adjustment);

  // Get the available space to place a bubble anchored to |anchor_rect| at
  // |arrow| inside |screen_rect|.
  static gfx::Size GetAvailableSpaceToPlaceBubble(BubbleBorder::Arrow arrow,
                                                  gfx::Rect anchor_rect,
                                                  gfx::Rect screen_rect);

  // Resize the bubble to fit its contents, and maybe move it if needed to keep
  // it anchored properly. This does not need to be invoked normally. This
  // should be called only if you need to force update the bounds of the widget
  // and/or position of the bubble, for example if the size of the bubble's
  // content view changed.
  // TODO(crbug.com/41493925) Not recommended; Use autosize in the constructor
  // instead.
  void SizeToContents();

 protected:
  // A helper class for logging UMA metrics related to bubbles.
  // The class logs metrics to:
  // 1. An aggregated histogram for all bubbles.
  // 2. A histogram specific to a bubble subclass when its name is provided.
  class VIEWS_EXPORT BubbleUmaLogger {
   public:
    BubbleUmaLogger();
    ~BubbleUmaLogger();

    void set_delegate(views::BubbleDialogDelegate* delegate) {
      delegate_ = delegate;
    }
    void set_bubble_view(views::View* view) { bubble_view_ = view; }

    void set_allowed_class_names_for_testing(
        const base::span<const char* const>& value) {
      allowed_class_names_for_testing_ = value;
    }

    std::optional<std::string> GetBubbleName() const;

    base::WeakPtr<BubbleUmaLogger> GetWeakPtr();

    // Logs a metric value to UMA histograms. This method logs to:
    // - "Bubble.All.{histogram_name}" for the general bubble metric.
    // - "Bubble.{bubble_name}.{histogram_name}" for a specific bubble
    //   subclass, if `bubble_name` is set.
    template <typename Value>
    void LogMetric(void (*uma_func)(std::string_view, Value),
                   std::string_view histogram_name,
                   Value value) const;

   private:
    std::optional<raw_ptr<views::View>> bubble_view_;
    std::optional<raw_ptr<views::BubbleDialogDelegate>> delegate_;
    std::optional<base::raw_span<const char* const>>
        allowed_class_names_for_testing_;
    base::WeakPtrFactory<BubbleUmaLogger> weak_factory_{this};
  };

  // Override this method if you want to position the bubble regardless of its
  // anchor, while retaining the other anchor view logic.
  virtual gfx::Rect GetBubbleBounds();

  // Override this to perform initialization after the Widget is created but
  // before it is shown.
  // TODO(pbos): Turn this into a (Once?)Callback and add set_init(cb).
  virtual void Init() {}

  BubbleUmaLogger& bubble_uma_logger() { return bubble_uma_logger_; }

  // Redeclarations of virtuals that BubbleDialogDelegate used to inherit from
  // WidgetObserver. These should not exist; do not add new overrides of them.
  // They exist to allow the WidgetObserver helper classes inside
  // BubbleDialogDelegate (AnchorWidgetObserver and BubbleWidgetObserver) to
  // forward specific events to BubbleDialogDelegate subclasses that were
  // overriding WidgetObserver methods from BubbleDialogDelegate. Whether they
  // are called for the anchor widget or the bubble widget and when is
  // deliberately unspecified.
  //
  // TODO(ellyjones): Get rid of these.
  virtual void OnWidgetClosing(Widget* widget) {}
  virtual void OnWidgetDestroying(Widget* widget) {}
  virtual void OnWidgetActivationChanged(Widget* widget, bool active) {}
  virtual void OnWidgetDestroyed(Widget* widget) {}
  virtual void OnWidgetBoundsChanged(Widget* widget, const gfx::Rect& bounds) {}
  virtual void OnWidgetVisibilityChanged(Widget* widget, bool visible) {}

 private:
  class AnchorViewObserver;
  class AnchorWidgetObserver;
  class BubbleWidgetObserver;

  FRIEND_TEST_ALL_PREFIXES(BubbleDialogDelegateViewTest,
                           VisibleWidgetShowsInkDropOnAttaching);
  FRIEND_TEST_ALL_PREFIXES(BubbleDialogDelegateViewTest,
                           AttachedWidgetShowsInkDropWhenVisible);
  FRIEND_TEST_ALL_PREFIXES(BubbleDialogDelegateViewTest,
                           MultipleBubbleAnchorHighlightTestInOrder);
  FRIEND_TEST_ALL_PREFIXES(BubbleDialogDelegateViewTest,
                           MultipleBubbleAnchorHighlightTestOutOfOrder);

  friend class AnchorViewObserver;
  friend class AnchorWidgetObserver;
  friend class BubbleWidgetObserver;
  friend class TestBubbleUmaLogger;

  friend class BubbleBorderDelegate;
  friend class BubbleWindowTargeter;

  // Notify the BubbleDialogDelegate about changes in the anchor Widget. You do
  // not need to call these yourself.
  void OnAnchorWidgetDestroying();
  void OnAnchorWidgetBoundsChanged();

  // Notify the BubbleDialogDelegate about changes in the bubble Widget. You do
  // not need to call these yourself.
  void OnBubbleWidgetClosing();
  void OnBubbleWidgetVisibilityChanged(bool visible);
  void OnBubbleWidgetActivationChanged(bool active);
  void OnBubbleWidgetPaintAsActiveChanged();

  void OnDeactivate();
  void UpdateFrameColor();

  // Notify this bubble that it is now the primary anchored bubble. When a new
  // bubble becomes the primary anchor, the previous primary silently loses its
  // primary status. This method is only called when this bubble becomes primary
  // after losing it.
  void NotifyAnchoredBubbleIsPrimary();

  void UpdateHighlightedButton(bool highlight);

  void SetAnchoredDialogKey();

  gfx::Rect GetDesiredBubbleBounds();

  gfx::Insets title_margins_;
  gfx::Insets footnote_margins_;
  BubbleBorder::Arrow arrow_ = BubbleBorder::NONE;
  BubbleBorder::Shadow shadow_;
  ui::ColorVariant color_ = ui::kColorBubbleBackground;
  raw_ptr<Widget> anchor_widget_ = nullptr;
  std::unique_ptr<AnchorViewObserver> anchor_view_observer_;
  std::unique_ptr<AnchorWidgetObserver> anchor_widget_observer_;
  std::unique_ptr<BubbleWidgetObserver> bubble_widget_observer_;
  bool adjust_if_offscreen_ = true;
  bool focus_traversable_from_anchor_view_ = true;
  ViewTracker highlighted_button_tracker_;
  ui::ImageModel main_image_;
  std::u16string subtitle_;
  bool subtitle_allow_character_break_ = false;
  ui::LayerType layer_type_ = ui::LayerType::LAYER_TEXTURED;

  // Whether the bubble should automatically resize to match its contents'
  // preferred size.
  bool autosize_ = false;

  // A flag controlling bubble closure on deactivation.
  bool close_on_deactivate_ = true;
  std::unique_ptr<CloseOnDeactivatePin::Pins> close_on_deactivate_pins_;

  // Whether the |anchor_widget_| (or the |highlighted_button_tracker_|, when
  // provided) should be highlighted when this bubble is shown.
  bool highlight_button_when_shown_ = true;

  mutable std::optional<gfx::Rect> anchor_rect_;

  bool accept_events_ = true;
  gfx::NativeView parent_window_ = gfx::NativeView();

  // By default, all BubbleDialogDelegates have parent windows.
  bool has_parent_ = true;

  // Pointer to this bubble's ClientView.
  raw_ptr<ClientView> client_view_ = nullptr;

#if BUILDFLAG(IS_MAC)
  // Special handler for close_on_deactivate() on Mac. Window (de)activation is
  // suppressed by the WindowServer when clicking rapidly, so the bubble must
  // monitor clicks as well for the desired behavior.
  std::unique_ptr<ui::BubbleCloser> mac_bubble_closer_;
#endif

  // Used to ensure the button remains anchored while this dialog is open.
  std::optional<Button::ScopedAnchorHighlight> button_anchor_highlight_;

  // The helper class that logs common bubble metrics.
  BubbleUmaLogger bubble_uma_logger_;

  std::optional<base::TimeTicks> bubble_created_time_;

  // Timestamp when the bubble turns visible.
  std::optional<base::TimeTicks> bubble_shown_time_;

  // Cumulated time of bubble being visible.
  base::TimeDelta bubble_shown_duration_;
};

// BubbleDialogDelegateView is a BubbleDialogDelegate that is also a View.
//
// DEPRECATED: Using this class makes it more challenging to reason about object
// ownership/lifetimes and promotes writing "fat" views that also contain
// business logic. Instead, use DialogModel if possible; otherwise, use separate
// subclasses of BubbleDialogDelegate and View to handle those interfaces'
// respective concerns.
//
// TODO(pbos): Migrate existing uses of BubbleDialogDelegateView to directly
// inherit or use BubbleDialogDelegate.
class VIEWS_EXPORT BubbleDialogDelegateView : public View,
                                              public BubbleDialogDelegate {
  METADATA_HEADER(BubbleDialogDelegateView, View)

 public:
  // Not named `PassKey` as `View::PassKey` already exists in this hierarchy.
  using BddvPassKey = base::PassKey<BubbleDialogDelegateView>;

  template <typename T>
  static bool IsBubbleDialogDelegateView(const BubbleDialogDelegateView* view) {
    return ui::metadata::IsClass<T, BubbleDialogDelegateView>(view);
  }

  // Create and initialize the bubble Widget(s) with proper bounds.
  // Like BubbleDialogDelegate::CreateBubble, the default ownership for now is
  // NATIVE_WIDGET_OWNS_WIDGET. If any other ownership mode is used, the
  // returned Widget's lifetime must be managed by the caller. This is usually
  // done by wrapping the pointer as a unique_ptr using base::WrapUnique().
  template <typename T>
  static Widget* CreateBubble(
      std::unique_ptr<T> delegate,
      Widget::InitParams::Ownership ownership =
          Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET) {
    CHECK(IsBubbleDialogDelegateView<T>(delegate.get()));
    return BubbleDialogDelegate::CreateBubble(std::move(delegate), ownership);
  }
  static Widget* CreateBubble(
      BubbleDialogDelegateView* bubble_delegate,
      Widget::InitParams::Ownership ownership =
          Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET);

  // For use with std::make_unique<>(). Callers still must be in the friend list
  // below, just as with the private constructor.
  explicit BubbleDialogDelegateView(
      BddvPassKey,
      View* anchor_view = nullptr,
      BubbleBorder::Arrow arrow = views::BubbleBorder::TOP_LEFT,
      BubbleBorder::Shadow shadow = BubbleBorder::DIALOG_SHADOW,
      bool autosize = false)
      : BubbleDialogDelegateView(anchor_view, arrow, shadow, autosize) {}

  BubbleDialogDelegateView(const BubbleDialogDelegateView&) = delete;
  BubbleDialogDelegateView& operator=(const BubbleDialogDelegateView&) = delete;
  ~BubbleDialogDelegateView() override;

  // BubbleDialogDelegate:
  View* GetContentsView() override;

  // View:
  Widget* GetWidget() override;
  const Widget* GetWidget() const override;

 protected:
  // Disallow overrides of GetMinimumSize and GetMaximumSize(). These would only
  // be called by the FrameView, but the BubbleFrameView ignores these. Bubbles
  // are not user-sizable and always size to their preferred size (plus any
  // border / frame).
  // View:
  gfx::Size GetMinimumSize() const final;
  gfx::Size GetMaximumSize() const final;

 private:
  FRIEND_TEST_ALL_PREFIXES(BubbleDelegateTest, CreateDelegate);
  FRIEND_TEST_ALL_PREFIXES(BubbleDelegateTest, NonClientHitTest);

  // DO NOT ADD TO THIS LIST!
  // These existing cases are "grandfathered in", but there shouldn't be more.
  // See comments atop class.
  friend class ::AccountChooserDialogView;
  friend class ::AnnouncementView;
  friend class ::AppDialogView;
  friend class ::BruschettaUninstallerView;
  friend class ::ChromeLabsBubbleView;
  friend class ::ColorPickerViewTest;
  friend class ::ContentSettingBubbleContents;
  friend class ::CriticalNotificationBubbleView;
  friend class ::CrostiniAnsibleSoftwareConfigView;
  friend class ::CrostiniExpiredContainerWarningView;
  friend class ::CrostiniForceCloseView;
  friend class ::CrostiniPackageInstallFailureView;
  friend class ::CrostiniRecoveryView;
  friend class ::CrostiniUninstallerView;
  friend class ::CrostiniUpdateFilesystemView;
  friend class ::DiceWebSigninInterceptionBubbleView;
  friend class ::ExtensionInstallDialogView;
  friend class ::ExtensionInstallFrictionDialogView;
  friend class ::ExtensionInstalledBubbleView;
  friend class ::ExtensionPopup;
  friend class ::ExtensionsMenuView;
  friend class ::FlyingIndicator;
  friend class ::GlobalErrorBubbleView;
  friend class ::HomePageUndoBubble;
  friend class ::MediaDialogView;
  friend class ::HatsNextWebDialog;
  friend class ::IncognitoClearBrowsingDataDialog;
  friend class ::LocationBarBubbleDelegateView;
  friend class ::NetworkProfileBubbleView;
  friend class ::PageInfoBubbleViewBase;
  friend class ::PermissionPromptBaseView;
  friend class ::PluginVmInstallerView;
  friend class ::ProfileCustomizationBubbleView;
  friend class ::ProfileMenuViewBase;
  friend class ::RemoveSuggestionBubbleDialogDelegateView;
  friend class ::StoragePressureBubbleView;
  friend class ::TabGroupEditorBubbleView;
  friend class ::TabHoverCardBubbleView;
  friend class ::TestBubbleView;
  friend class ::ToolbarActionHoverCardBubbleView;
  friend class ::ToolbarActionsBarBubbleViews;
  friend class ::ScreenshotSurfaceTestDialog;
  friend class ::WebBubbleView;
  friend class ::WebUIBubbleDialogView;
  FRIEND_TEST_ALL_PREFIXES(::InProcessBrowserTest,
                           RunsScheduledLayoutOnAnchoredBubbles);
  friend class ::ambient_signin::AmbientSigninBubbleView;
  friend class ::arc::ArcSplashScreenDialogView;
  friend class ::arc::BaseDialogDelegateView;
  friend class ::arc::ResizeConfirmationDialogView;
  friend class ::arc::RoundedCornerBubbleDialogDelegateView;
  friend class ::arc::input_overlay::DeleteEditShortcut;
  friend class ::arc::input_overlay::RichNudge;
  friend class ::ash::AnchoredNudge;
  friend class ::ash::ContextualNudge;
  friend class ::ash::DictationBubbleView;
  friend class ::ash::FaceGazeBubbleView;
  friend class ::ash::GameDashboardMainMenuView;
  friend class ::ash::HelpBubbleViewAsh;
  friend class ::ash::ImeModeIndicatorView;
  friend class ::ash::KioskAppInstructionBubble;
  friend class ::ash::MouseKeysBubbleView;
  friend class ::ash::NetworkInfoBubble;
  friend class ::ash::NetworkStateListInfoBubble;
  friend class ::ash::PaletteWelcomeBubbleView;
  friend class ::ash::QuickInsertCapsLockStateView;
  friend class ::ash::QuickInsertPreviewBubbleView;
  friend class ::ash::ShelfBubble;
  friend class ::ash::TestBubbleDialogDelegateView;
  friend class ::ash::TestBubbleDialogDelegate;
  friend class ::ash::TrayBubbleView;
  FRIEND_TEST_ALL_PREFIXES(::ash::OverviewSessionTest,
                           DoNotHideBubbleTransient);
  FRIEND_TEST_ALL_PREFIXES(::ash::ResizeShadowAndCursorTest,
                           DefaultCursorOnBubbleWidgetCorners);
  FRIEND_TEST_ALL_PREFIXES(::ash::SnapGroupOverviewTest,
                           BubbleTransientIsVisibleInOverview);
  FRIEND_TEST_ALL_PREFIXES(
      ::ash::SnapGroupDesksTest,
      NoCrashWhenDraggingOverviewGroupItemWithBubbleToAnotherDesk);
  FRIEND_TEST_ALL_PREFIXES(
      ::ash::SnapGroupTest,
      NoCrashWhenReSnappingSecondaryToPrimaryWithTransient);
  friend class ::ash::sharesheet::SharesheetBubbleView;
  friend class ::autofill::CardUnmaskAuthenticationSelectionDialogView;
  friend class ::autofill::CardUnmaskPromptViews;
  friend class ::autofill::LocalCardMigrationDialogView;
  friend class ::autofill::LocalCardMigrationErrorDialogView;
  friend class ::captions::CaptionBubble;
  friend class ::chromeos::MultitaskMenu;
  friend class ::lens::LensPreselectionBubble;
  friend class ::lens::LensRegionSearchInstructionsView;
  friend class ::media_router::CastDialogView;
  friend class ::media_router::MediaRemotingDialogView;
  friend class ::send_tab_to_self::SendTabToSelfToolbarBubbleView;
  friend class ::toasts::ToastView;
  friend class ::ui::ime::AnnouncementView;
  friend class ::ui::ime::CandidateWindowView;
  friend class ::ui::ime::GrammarSuggestionWindow;
  friend class ::ui::ime::InfolistWindow;
  friend class ::ui::ime::SuggestionWindowView;
  friend class ::ui::ime::UndoWindow;
  friend class ::user_education::HelpBubbleView;
  friend class ::user_education::test::TestCustomHelpBubbleView;
  friend class ::webid::AccountSelectionBubbleView;
  friend class AnchorTestBubbleDialogDelegateView;
  friend class FocusManagerTestBubbleDialogDelegateView;
  friend class FrameViewTestBubbleDialogDelegateView;
  friend class InfoBubble;
  friend class InteractionSequenceViewsTest;
  friend class TestBubbleDialogDelegateView;
  friend class TestBubbleView;
  friend class TouchSelectionMenuViews;
  FRIEND_TEST_ALL_PREFIXES(BubbleDialogDelegateViewInteractiveTest,
                           BubbleAndParentNotActiveSimultaneously);
  FRIEND_TEST_ALL_PREFIXES(BubbleDialogDelegateViewTest,
                           ClientViewIsPaintedToLayer);
  FRIEND_TEST_ALL_PREFIXES(WidgetFocusObserverTest, Bubble);
  friend class examples::DialogExampleDelegate<BubbleDialogDelegateView>;
  friend class examples::ExampleBubble;
  friend class examples::LoginBubbleDialogView;
  friend class test::SimpleBubbleView;
  friend class test::TestBubbleView;
  friend class test::WidgetTestBubbleDialogDelegateView;
  FRIEND_TEST_ALL_PREFIXES(test::DesktopWidgetTestInteractive,
                           FocusChangesOnBubble);
  FRIEND_TEST_ALL_PREFIXES(test::InteractionTestUtilViewsTest, ActivateSurface);
  FRIEND_TEST_ALL_PREFIXES(test::InteractionTestUtilViewsTest, Confirm);

  // |shadow| usually doesn't need to be explicitly set, just uses the default
  // argument. Unless on Mac when the bubble needs to use Views base shadow,
  // override it with suitable bubble border type.
  explicit BubbleDialogDelegateView(
      View* anchor_view = nullptr,
      BubbleBorder::Arrow arrow = views::BubbleBorder::TOP_LEFT,
      BubbleBorder::Shadow shadow = BubbleBorder::DIALOG_SHADOW,
      bool autosize = false);

  static BddvPassKey CreatePassKey() { return BddvPassKey(); }
};

BEGIN_VIEW_BUILDER(VIEWS_EXPORT, BubbleDialogDelegateView, View)
VIEW_BUILDER_PROPERTY(ax::mojom::Role, AccessibleWindowRole)
VIEW_BUILDER_PROPERTY(std::u16string, AccessibleTitle)
VIEW_BUILDER_PROPERTY(bool, CanMaximize)
VIEW_BUILDER_PROPERTY(bool, CanMinimize)
VIEW_BUILDER_PROPERTY(bool, CanResize)
VIEW_BUILDER_VIEW_TYPE_PROPERTY(views::View, ExtraView)
VIEW_BUILDER_VIEW_TYPE_PROPERTY(views::View, FootnoteView)
VIEW_BUILDER_PROPERTY(bool, FocusTraversesOut)
VIEW_BUILDER_PROPERTY(bool, EnableArrowKeyTraversal)
VIEW_BUILDER_PROPERTY(ui::ImageModel, Icon)
VIEW_BUILDER_PROPERTY(ui::ImageModel, AppIcon)
VIEW_BUILDER_PROPERTY(ui::ImageModel, MainImage)
VIEW_BUILDER_PROPERTY(ui::mojom::ModalType, ModalType)
VIEW_BUILDER_PROPERTY(bool, OwnedByWidget)
VIEW_BUILDER_PROPERTY(bool, ShowCloseButton)
VIEW_BUILDER_PROPERTY(bool, ShowIcon)
VIEW_BUILDER_PROPERTY(bool, ShowTitle)
VIEW_BUILDER_OVERLOAD_METHOD_CLASS(WidgetDelegate,
                                   SetTitle,
                                   const std::u16string&)
VIEW_BUILDER_OVERLOAD_METHOD_CLASS(WidgetDelegate, SetTitle, int)
#if defined(USE_AURA)
VIEW_BUILDER_PROPERTY(bool, CenterTitle)
#endif
VIEW_BUILDER_PROPERTY(int, Buttons)
VIEW_BUILDER_PROPERTY(int, DefaultButton)
VIEW_BUILDER_METHOD(SetButtonLabel, ui::mojom::DialogButton, std::u16string)
VIEW_BUILDER_METHOD(SetButtonEnabled, ui::mojom::DialogButton, bool)
VIEW_BUILDER_METHOD(set_margins, gfx::Insets)
VIEW_BUILDER_METHOD(set_use_round_corners, bool)
VIEW_BUILDER_METHOD(set_corner_radius, int)
VIEW_BUILDER_METHOD(set_draggable, bool)
VIEW_BUILDER_METHOD(set_use_custom_frame, bool)
VIEW_BUILDER_METHOD(set_fixed_width, int)
VIEW_BUILDER_METHOD(set_highlight_button_when_shown, bool)
VIEW_BUILDER_PROPERTY(base::OnceClosure, AcceptCallback)
VIEW_BUILDER_PROPERTY(base::OnceClosure, CancelCallback)
VIEW_BUILDER_PROPERTY(base::OnceClosure, CloseCallback)
VIEW_BUILDER_PROPERTY(const gfx::Insets&, ButtonRowInsets)
END_VIEW_BUILDER

}  // namespace views

DEFINE_VIEW_BUILDER(VIEWS_EXPORT, BubbleDialogDelegateView)

#endif  // UI_VIEWS_BUBBLE_BUBBLE_DIALOG_DELEGATE_VIEW_H_