File: access_code_cast_dialog.cc

package info (click to toggle)
chromium 138.0.7204.183-1~deb12u1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm-proposed-updates
  • size: 6,080,960 kB
  • sloc: cpp: 34,937,079; ansic: 7,176,967; javascript: 4,110,704; python: 1,419,954; asm: 946,768; xml: 739,971; pascal: 187,324; sh: 89,623; perl: 88,663; objc: 79,944; sql: 50,304; cs: 41,786; fortran: 24,137; makefile: 21,811; php: 13,980; tcl: 13,166; yacc: 8,925; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (280 lines) | stat: -rw-r--r-- 10,759 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
// Copyright 2022 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/ui/webui/access_code_cast/access_code_cast_dialog.h"

#include "base/json/json_writer.h"
#include "base/memory/weak_ptr.h"
#include "base/values.h"
#include "chrome/browser/media/router/discovery/access_code/access_code_cast_feature.h"
#include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/views/chrome_constrained_window_views_client.h"
#include "chrome/browser/ui/views/chrome_web_dialog_view.h"
#include "chrome/browser/ui/webui/access_code_cast/access_code_cast_ui.h"
#include "chrome/common/webui_url_constants.h"
#include "components/web_modal/web_contents_modal_dialog_host.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/web_contents.h"
#include "ui/display/display.h"
#include "ui/display/screen.h"
#include "ui/views/layout/layout_provider.h"
#include "url/gurl.h"

using web_modal::ModalDialogHost;

namespace media_router {

namespace {

void SetCurrentDialog(base::WeakPtr<AccessCodeCastDialog> dialog) {
  // Keeps track of the dialog that is currently being displayed.
  static base::NoDestructor<base::WeakPtr<AccessCodeCastDialog>>
      current_instance;
  if (*current_instance) {
    // Closing the dialog will cause the dialog to delete itself.
    (*current_instance)->CloseDialogWidget();
  }
  if (dialog) {
    *current_instance = std::move(dialog);
  }
}

void UpdateDialogPosition(views::Widget* widget,
                          content::WebContents* web_contents) {
  auto* dialog_host =
      CreateChromeConstrainedWindowViewsClient()->GetModalDialogHost(
          web_contents->GetTopLevelNativeWindow());
  views::Widget* host_widget =
      views::Widget::GetWidgetForNativeView(dialog_host->GetHostView());

  // If the host view is not backed by a Views::Widget, just update the widget
  // size.
  auto size = widget->GetRootView()->GetPreferredSize();
  if (!host_widget) {
    widget->SetSize(size);
    return;
  }

  // Get the outer browser window for the web_contents.
  auto* browser_window =
      BrowserWindow::FindBrowserWindowWithWebContents(web_contents);
  auto window_bounds = browser_window->GetBounds();

  gfx::Point position = dialog_host->GetDialogPosition(size);
  // Align the first row of pixels inside the border. This is the apparent top
  // of the dialog.
  position.set_y(position.y() -
                 widget->non_client_view()->frame_view()->GetInsets().top());

  if (widget->is_top_level()) {
    position += host_widget->GetClientAreaBoundsInScreen().OffsetFromOrigin();
    // Move the dialog to the center of the browser window.
    auto new_x = window_bounds.x() + (window_bounds.width() - size.width()) / 2;
    position.set_x(new_x);
    // If the dialog extends partially off any display, clamp its position to
    // be fully visible within that display. If the dialog doesn't intersect
    // with any display clamp its position to be fully on the nearest display.
    gfx::Rect display_rect = gfx::Rect(position, size);
    const display::Display display =
        display::Screen::GetScreen()->GetDisplayNearestView(
            dialog_host->GetHostView());
    const gfx::Rect work_area = display.work_area();

    if (!work_area.Contains(display_rect)) {
      display_rect.AdjustToFit(work_area);
    }
    position = display_rect.origin();
  }

  widget->SetBounds(gfx::Rect(position, size));
}

}  // namespace

// The corner radius for system dialogs.
constexpr int kSystemDialogCornerRadiusDp = 12;

// The default width, height without footnote, height with footnote for the
// dialog container.
constexpr gfx::Size kDialogSizeWithoutFootnote{448, 295};
constexpr gfx::Size kDialogSizeWithFootnote{448, 330};

// static
bool AccessCodeCastDialog::block_widget_activation_changed_for_test_ = false;

AccessCodeCastDialog::AccessCodeCastDialog(
    const CastModeSet& cast_mode_set,
    std::unique_ptr<MediaRouteStarter> media_route_starter)
    : cast_mode_set_(cast_mode_set),
      media_route_starter_(std::move(media_route_starter)),
      web_contents_(media_route_starter_->GetWebContents()),
      context_(media_route_starter_->GetProfile()) {
  DCHECK(media_route_starter_) << "Must have a media route starter!";
  DCHECK(!cast_mode_set_.empty())
      << "Must have at least one available casting mode!";
  DCHECK(*cast_mode_set_.begin() == MediaCastMode::DESKTOP_MIRROR ||
         web_contents_)
      << "Web contents must be set for non desktop-mode casting!";
  set_can_resize(false);
  set_dialog_args("{}");
  set_dialog_content_url(GURL(chrome::kChromeUIAccessCodeCastURL));
  set_dialog_frame_kind(FrameKind::kDialog);
  set_show_close_button(false);
  set_show_dialog_title(false);

  base::TimeDelta duration = GetAccessCodeDeviceDurationPref(context_);
  const bool remember_devices = duration != base::Seconds(0);
  set_dialog_size(remember_devices ? kDialogSizeWithFootnote
                                   : kDialogSizeWithoutFootnote);
}

AccessCodeCastDialog::~AccessCodeCastDialog() = default;

void AccessCodeCastDialog::ShowWebDialog(AccessCodeCastDialogMode dialog_mode) {
  // After a dialog is shown, |media_route_starter_| is transferred to the
  // associated |AccessCodeCastUI| - see |OnDialogShown| below. Since the c'tor
  // ensures that a |MediaRouteStarter| is passed in, if |media_route_starter_|
  // is nullptr, it means that |ShowWebDialog| was already called.
  DCHECK(media_route_starter_) << "Cannot show dialog more than once!";
  if (!media_route_starter_) {
    return;
  }

  auto extra_params = CreateParams(dialog_mode);

  dialog_creation_timestamp_ = base::Time::Now();
  gfx::NativeWindow dialog_window = chrome::ShowWebDialogWithParams(
      GetParentView(), context_, this,
      std::make_optional<views::Widget::InitParams>(std::move(extra_params)));

  dialog_widget_ = views::Widget::GetWidgetForNativeWindow(dialog_window);
  widget_observation_.Observe(dialog_widget_.get());

  if (dialog_mode == AccessCodeCastDialogMode::kBrowserStandard &&
      web_contents_) {
    UpdateDialogPosition(dialog_widget_, web_contents_);
  }
}

// static
void AccessCodeCastDialog::Show(
    const media_router::CastModeSet& cast_mode_set,
    std::unique_ptr<media_router::MediaRouteStarter> media_route_starter,
    AccessCodeCastDialogOpenLocation open_location,
    AccessCodeCastDialogMode dialog_mode) {
  std::unique_ptr<AccessCodeCastDialog> dialog =
      std::make_unique<AccessCodeCastDialog>(cast_mode_set,
                                             std::move(media_route_starter));
  dialog->ShowWebDialog(dialog_mode);
  // Release the pointer from the unique_ptr after ShowWebDialog() since now the
  // lifetime of the dialog is being managed by the WebDialog Delegate. The
  // dialog will delete itself when OnDialogClosed() is called.
  base::WeakPtr<AccessCodeCastDialog> new_dialog =
      dialog.release()->GetWeakPtr();
  AccessCodeCastMetrics::RecordDialogOpenLocation(open_location);
  SetCurrentDialog(std::move(new_dialog));
}

// static
void AccessCodeCastDialog::ShowForDesktopMirroring(
    AccessCodeCastDialogOpenLocation open_location) {
  CastModeSet desktop_mode = {MediaCastMode::DESKTOP_MIRROR};
  std::unique_ptr<MediaRouteStarter> starter =
      std::make_unique<MediaRouteStarter>(
          MediaRouterUIParameters(desktop_mode, nullptr));
  Show(desktop_mode, std::move(starter), open_location,
       AccessCodeCastDialogMode::kSystem);
}

views::Widget::InitParams AccessCodeCastDialog::CreateParams(
    AccessCodeCastDialogMode dialog_mode) {
  views::Widget::InitParams params(
      views::Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET);
  params.remove_standard_frame = true;
  // If we are acting as a system dialog, use the appropriate corner radius.
  // Otherwise, the widget will default to the correct value for browser
  // dialogs.
  if (dialog_mode == AccessCodeCastDialogMode::kSystem) {
    params.corner_radius = kSystemDialogCornerRadiusDp;
  }
  params.type = views::Widget::InitParams::Type::TYPE_BUBBLE;
  // Make sure the dialog border is rendered correctly
  params.opacity = views::Widget::InitParams::WindowOpacity::kTranslucent;

  return params;
}

void AccessCodeCastDialog::CloseDialogWidget() {
  dialog_widget_->Close();
}

base::WeakPtr<AccessCodeCastDialog> AccessCodeCastDialog::GetWeakPtr() {
  return weak_ptr_factory_.GetWeakPtr();
}

// views::WidgetObserver:
void AccessCodeCastDialog::OnWidgetActivationChanged(views::Widget* widget,
                                                     bool active) {
  if (block_widget_activation_changed_for_test_) {
    return;
  }
  DCHECK(dialog_widget_)
      << "dialog_widget_ must be set exactly once during dialog setup";
  // Close the dialog only if it is no longer active and it isn't already
  // closing.
  if (dialog_widget_ && !active && !closing_dialog_) {
    AccessCodeCastMetrics::RecordDialogCloseReason(
        AccessCodeCastDialogCloseReason::kFocus);
    dialog_widget_->Close();
  }
}

void AccessCodeCastDialog::OnDialogShown(content::WebUI* webui) {
  webui_ = webui;
  AccessCodeCastUI* controller =
      webui_->GetController()->GetAs<AccessCodeCastUI>();
  controller->SetCastModeSet(cast_mode_set_);
  controller->SetDialogCreationTimestamp(dialog_creation_timestamp_);
  controller->SetMediaRouteStarter(std::move(media_route_starter_));
}

void AccessCodeCastDialog::OnCloseContents(content::WebContents* source,
                                           bool* out_close_dialog) {
  *out_close_dialog = true;
  closing_dialog_ = true;
}

// Ensure the WebUI dialog has camera access
void AccessCodeCastDialog::RequestMediaAccessPermission(
    content::WebContents* web_contents,
    const content::MediaStreamRequest& request,
    content::MediaResponseCallback callback) {
  MediaCaptureDevicesDispatcher::GetInstance()->ProcessMediaAccessRequest(
      web_contents, request, std::move(callback), nullptr /* extension */);
}

bool AccessCodeCastDialog::CheckMediaAccessPermission(
    content::RenderFrameHost* render_frame_host,
    const url::Origin& security_origin,
    blink::mojom::MediaStreamType type) {
  return true;
}

gfx::NativeView AccessCodeCastDialog::GetParentView() {
  gfx::NativeView parent = gfx::NativeView();

  if (web_contents_) {
    views::Widget* widget = views::Widget::GetWidgetForNativeWindow(
        web_contents_->GetTopLevelNativeWindow());
    DCHECK(widget) << "Could not find a parent widget!";
    if (widget) {
      parent = widget->GetNativeView();
    }
  }

  return parent;
}

}  // namespace media_router