标签:The show lower bool search sheet tst tac rda
electron-master\electron-master\shell\browser\api\atom_api_web_contents.cc
// Copyright (c) 2014 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "shell/browser/api/atom_api_web_contents.h"
#include
#include set>
#include string>
#include
#include
#include "base/message_loop/message_loop_current.h"
#include "base/no_destructor.h"
#include "base/optional.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_restrictions.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/values.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/ssl/security_state_tab_helper.h"
#include "content/browser/frame_host/frame_tree_node.h" // nogncheck
#include "content/browser/frame_host/render_frame_host_manager.h" // nogncheck
#include "content/browser/renderer_host/render_widget_host_impl.h" // nogncheck
#include "content/browser/renderer_host/render_widget_host_view_base.h" // nogncheck
#include "content/common/widget_messages.h"
#include "content/public/browser/child_process_security_policy.h"
#include "content/public/browser/download_request_utils.h"
#include "content/public/browser/favicon_status.h"
#include "content/public/browser/native_web_keyboard_event.h"
#include "content/public/browser/navigation_details.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/plugin_service.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/render_widget_host.h"
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/service_worker_context.h"
#include "content/public/browser/site_instance.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/context_menu_params.h"
#include "electron/buildflags/buildflags.h"
#include "electron/shell/common/api/api.mojom.h"
#include "mojo/public/cpp/bindings/associated_remote.h"
#include "mojo/public/cpp/system/platform_handle.h"
#include "ppapi/buildflags/buildflags.h"
#include "shell/browser/api/atom_api_browser_window.h"
#include "shell/browser/api/atom_api_debugger.h"
#include "shell/browser/api/atom_api_session.h"
#include "shell/browser/atom_autofill_driver_factory.h"
#include "shell/browser/atom_browser_client.h"
#include "shell/browser/atom_browser_context.h"
#include "shell/browser/atom_browser_main_parts.h"
#include "shell/browser/atom_javascript_dialog_manager.h"
#include "shell/browser/atom_navigation_throttle.h"
#include "shell/browser/browser.h"
#include "shell/browser/child_web_contents_tracker.h"
#include "shell/browser/lib/bluetooth_chooser.h"
#include "shell/browser/native_window.h"
#include "shell/browser/session_preferences.h"
#include "shell/browser/ui/drag_util.h"
#include "shell/browser/ui/inspectable_web_contents.h"
#include "shell/browser/ui/inspectable_web_contents_view.h"
#include "shell/browser/web_contents_permission_helper.h"
#include "shell/browser/web_contents_preferences.h"
#include "shell/browser/web_contents_zoom_controller.h"
#include "shell/browser/web_view_guest_delegate.h"
#include "shell/common/api/atom_api_native_image.h"
#include "shell/common/color_util.h"
#include "shell/common/gin_converters/blink_converter.h"
#include "shell/common/gin_converters/callback_converter.h"
#include "shell/common/gin_converters/content_converter.h"
#include "shell/common/gin_converters/file_path_converter.h"
#include "shell/common/gin_converters/gfx_converter.h"
#include "shell/common/gin_converters/gurl_converter.h"
#include "shell/common/gin_converters/image_converter.h"
#include "shell/common/gin_converters/net_converter.h"
#include "shell/common/gin_converters/value_converter.h"
#include "shell/common/gin_helper/dictionary.h"
#include "shell/common/gin_helper/object_template_builder.h"
#include "shell/common/mouse_util.h"
#include "shell/common/node_includes.h"
#include "shell/common/options_switches.h"
#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
#include "third_party/blink/public/common/page/page_zoom.h"
#include "third_party/blink/public/mojom/frame/find_in_page.mojom.h"
#include "third_party/blink/public/mojom/frame/fullscreen.mojom.h"
#include "third_party/blink/public/platform/web_cursor_info.h"
#include "third_party/blink/public/platform/web_input_event.h"
#include "ui/display/screen.h"
#include "ui/events/base_event_utils.h"
#if BUILDFLAG(ENABLE_OSR)
#include "shell/browser/osr/osr_render_widget_host_view.h"
#include "shell/browser/osr/osr_web_contents_view.h"
#endif
#if !defined(OS_MACOSX)
#include "ui/aura/window.h"
#else
#include "ui/base/cocoa/defaults_utils.h"
#endif
#if defined(OS_LINUX)
#include "ui/views/linux_ui/linux_ui.h"
#endif
#if defined(OS_LINUX) || defined(OS_WIN)
#include "third_party/blink/public/mojom/renderer_preferences.mojom.h"
#include "ui/gfx/font_render_params.h"
#endif
#if BUILDFLAG(ENABLE_PRINTING)
#include "chrome/browser/printing/print_view_manager_basic.h"
#include "components/printing/common/print_messages.h"
#endif
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
#include "shell/browser/extensions/atom_extension_web_contents_observer.h"
#endif
namespace gin {
#if BUILDFLAG(ENABLE_PRINTING)
template
struct Converter<:printerbasicinfo> {
static v8::Local<:value> ToV8(v8::Isolate* isolate,
const printing::PrinterBasicInfo& val) {
gin_helper::Dictionary dict = gin::Dictionary::CreateEmpty(isolate);
dict.Set("name", val.printer_name);
dict.Set("displayName", val.display_name);
dict.Set("description", val.printer_description);
dict.Set("status", val.printer_status);
dict.Set("isDefault", val.is_default ? true : false);
dict.Set("options", val.options);
return dict.GetHandle();
}
};
template
struct Converter<:margintype> {
static bool FromV8(v8::Isolate* isolate,
v8::Local<:value> val,
printing::MarginType* out) {
std::string type;
if (ConvertFromV8(isolate, val, &type)) {
if (type == "default") {
*out = printing::DEFAULT_MARGINS;
return true;
}
if (type == "none") {
*out = printing::NO_MARGINS;
return true;
}
if (type == "printableArea") {
*out = printing::PRINTABLE_AREA_MARGINS;
return true;
}
if (type == "custom") {
*out = printing::CUSTOM_MARGINS;
return true;
}
}
return false;
}
};
template
struct Converter<:duplexmode> {
static bool FromV8(v8::Isolate* isolate,
v8::Local<:value> val,
printing::DuplexMode* out) {
std::string mode;
if (ConvertFromV8(isolate, val, &mode)) {
if (mode == "simplex") {
*out = printing::SIMPLEX;
return true;
}
if (mode == "longEdge") {
*out = printing::LONG_EDGE;
return true;
}
if (mode == "shortEdge") {
*out = printing::SHORT_EDGE;
return true;
}
}
return false;
}
};
#endif
template
struct Converter {
static v8::Local<:value> ToV8(v8::Isolate* isolate,
WindowOpenDisposition val) {
std::string disposition = "other";
switch (val) {
case WindowOpenDisposition::CURRENT_TAB:
disposition = "default";
break;
case WindowOpenDisposition::NEW_FOREGROUND_TAB:
disposition = "foreground-tab";
break;
case WindowOpenDisposition::NEW_BACKGROUND_TAB:
disposition = "background-tab";
break;
case WindowOpenDisposition::NEW_POPUP:
case WindowOpenDisposition::NEW_WINDOW:
disposition = "new-window";
break;
case WindowOpenDisposition::SAVE_TO_DISK:
disposition = "save-to-disk";
break;
default:
break;
}
return gin::ConvertToV8(isolate, disposition);
}
};
template
struct Converter<:savepagetype> {
static bool FromV8(v8::Isolate* isolate,
v8::Local<:value> val,
content::SavePageType* out) {
std::string save_type;
if (!ConvertFromV8(isolate, val, &save_type))
return false;
save_type = base::ToLowerASCII(save_type);
if (save_type == "htmlonly") {
*out = content::SAVE_PAGE_TYPE_AS_ONLY_HTML;
} else if (save_type == "htmlcomplete") {
*out = content::SAVE_PAGE_TYPE_AS_COMPLETE_HTML;
} else if (save_type == "mhtml") {
*out = content::SAVE_PAGE_TYPE_AS_MHTML;
} else {
return false;
}
return true;
}
};
template
struct Converter<:api::webcontents::type> {
static v8::Local<:value> ToV8(v8::Isolate* isolate,
electron::api::WebContents::Type val) {
using Type = electron::api::WebContents::Type;
std::string type;
switch (val) {
case Type::BACKGROUND_PAGE:
type = "backgroundPage";
break;
case Type::BROWSER_WINDOW:
type = "window";
break;
case Type::BROWSER_VIEW:
type = "browserView";
break;
case Type::REMOTE:
type = "remote";
break;
case Type::WEB_VIEW:
type = "webview";
break;
case Type::OFF_SCREEN:
type = "offscreen";
break;
default:
break;
}
return gin::ConvertToV8(isolate, type);
}
static bool FromV8(v8::Isolate* isolate,
v8::Local<:value> val,
electron::api::WebContents::Type* out) {
using Type = electron::api::WebContents::Type;
std::string type;
if (!ConvertFromV8(isolate, val, &type))
return false;
if (type == "backgroundPage") {
*out = Type::BACKGROUND_PAGE;
} else if (type == "browserView") {
*out = Type::BROWSER_VIEW;
} else if (type == "webview") {
*out = Type::WEB_VIEW;
#if BUILDFLAG(ENABLE_OSR)
} else if (type == "offscreen") {
*out = Type::OFF_SCREEN;
#endif
} else {
return false;
}
return true;
}
};
template
struct Converter> {
static v8::Local<:value> ToV8(
v8::Isolate* isolate,
const scoped_refptr<:devtoolsagenthost>& val) {
gin_helper::Dictionary dict(isolate, v8::Object::New(isolate));
dict.Set("id", val->GetId());
dict.Set("url", val->GetURL().spec());
return dict.GetHandle();
}
};
} // namespace gin
namespace electron {
namespace api {
namespace {
// Called when CapturePage is done.
void OnCapturePageDone(gin_helper::Promise<:image> promise,
const SkBitmap& bitmap) {
// Hack to enable transparency in captured image
promise.Resolve(gfx::Image::CreateFrom1xBitmap(bitmap));
}
base::Optionalbase::TimeDelta> GetCursorBlinkInterval() {
#if defined(OS_MACOSX)
base::TimeDelta interval;
if (ui::TextInsertionCaretBlinkPeriod(&interval))
return interval;
#elif defined(OS_LINUX)
if (auto* linux_ui = views::LinuxUI::instance())
return linux_ui->GetCursorBlinkInterval();
#elif defined(OS_WIN)
const auto system_msec = ::GetCaretBlinkTime();
if (system_msec != 0) {
return (system_msec == INFINITE)
? base::TimeDelta()
: base::TimeDelta::FromMilliseconds(system_msec);
}
#endif
return base::nullopt;
}
} // namespace
WebContents::WebContents(v8::Isolate* isolate,
content::WebContents* web_contents)
: content::WebContentsObserver(web_contents),
type_(Type::REMOTE),
weak_factory_(this) {
web_contents->SetUserAgentOverride(GetBrowserContext()->GetUserAgent(),
false);
Init(isolate);
AttachAsUserData(web_contents);
InitZoomController(web_contents, gin::Dictionary::CreateEmpty(isolate));
registry_.AddInterface(base::BindRepeating(&WebContents::BindElectronBrowser,
base::Unretained(this)));
bindings_.set_connection_error_handler(base::BindRepeating(
&WebContents::OnElectronBrowserConnectionError, base::Unretained(this)));
}
WebContents::WebContents(v8::Isolate* isolate,
std::unique_ptr<:webcontents> web_contents,
Type type)
: content::WebContentsObserver(web_contents.get()),
type_(type),
weak_factory_(this) {
DCHECK(type != Type::REMOTE)
"Can‘t take ownership of a remote WebContents";
auto session = Session::CreateFrom(isolate, GetBrowserContext());
session_.Reset(isolate, session.ToV8());
InitWithSessionAndOptions(isolate, std::move(web_contents), session,
gin::Dictionary::CreateEmpty(isolate));
}
WebContents::WebContents(v8::Isolate* isolate,
const gin_helper::Dictionary& options)
: weak_factory_(this) {
// Read options.
options.Get("backgroundThrottling", &background_throttling_);
// Get type
options.Get("type", &type_);
#if BUILDFLAG(ENABLE_OSR)
bool b = false;
if (options.Get(options::kOffscreen, &b) && b)
type_ = Type::OFF_SCREEN;
#endif
// Init embedder earlier
options.Get("embedder", &embedder_);
// Whether to enable DevTools.
options.Get("devTools", &enable_devtools_);
// BrowserViews are not attached to a window initially so they should start
// off as hidden. This is also important for compositor recycling. See:
// https://github.com/electron/electron/pull/21372
bool initially_shown = type_ != Type::BROWSER_VIEW;
options.Get(options::kShow, &initially_shown);
// Obtain the session.
std::string partition;
gin::Handle session;
if (options.Get("session", &session) && !session.IsEmpty()) {
} else if (options.Get("partition", &partition)) {
session = Session::FromPartition(isolate, partition);
} else {
// Use the default session if not specified.
session = Session::FromPartition(isolate, "");
}
session_.Reset(isolate, session.ToV8());
std::unique_ptr<:webcontents> web_contents;
if (IsGuest()) {
scoped_refptr<:siteinstance> site_instance =
content::SiteInstance::CreateForURL(session->browser_context(),
GURL("chrome-guest://fake-host"));
content::WebContents::CreateParams params(session->browser_context(),
site_instance);
guest_delegate_ =
std::make_unique(embedder_->web_contents(), this);
params.guest_delegate = guest_delegate_.get();
#if BUILDFLAG(ENABLE_OSR)
if (embedder_ && embedder_->IsOffScreen()) {
auto* view = new OffScreenWebContentsView(
false,
base::BindRepeating(&WebContents::OnPaint, base::Unretained(this)));
params.view = view;
params.delegate_view = view;
web_contents = content::WebContents::Create(params);
view->SetWebContents(web_contents.get());
} else {
#endif
web_contents = content::WebContents::Create(params);
#if BUILDFLAG(ENABLE_OSR)
}
} else if (IsOffScreen()) {
bool transparent = false;
options.Get("transparent", &transparent);
content::WebContents::CreateParams params(session->browser_context());
auto* view = new OffScreenWebContentsView(
transparent,
base::BindRepeating(&WebContents::OnPaint, base::Unretained(this)));
params.view = view;
params.delegate_view = view;
web_contents = content::WebContents::Create(params);
view->SetWebContents(web_contents.get());
#endif
} else {
content::WebContents::CreateParams params(session->browser_context());
params.initially_hidden = !initially_shown;
web_contents = content::WebContents::Create(params);
}
InitWithSessionAndOptions(isolate, std::move(web_contents), session, options);
}
void WebContents::InitZoomController(content::WebContents* web_contents,
const gin_helper::Dictionary& options) {
WebContentsZoomController::CreateForWebContents(web_contents);
zoom_controller_ = WebContentsZoomController::FromWebContents(web_contents);
double zoom_factor;
if (options.Get(options::kZoomFactor, &zoom_factor))
zoom_controller_->SetDefaultZoomFactor(zoom_factor);
}
void WebContents::InitWithSessionAndOptions(
v8::Isolate* isolate,
std::unique_ptr<:webcontents> owned_web_contents,
gin::Handle session,
const gin_helper::Dictionary& options) {
Observe(owned_web_contents.get());
// TODO(zcbenz): Make InitWithWebContents take unique_ptr.
// At the time of writing we are going through a refactoring and I don‘t want
// to make other people‘s work harder.
InitWithWebContents(owned_web_contents.release(), session->browser_context(),
IsGuest());
managed_web_contents()->GetView()->SetDelegate(this);
auto* prefs = web_contents()->GetMutableRendererPrefs();
prefs->accept_languages = g_browser_process->GetApplicationLocale();
#if defined(OS_LINUX) || defined(OS_WIN)
// Update font settings.
static const base::NoDestructor<:fontrenderparams>params(
gfx::GetFontRenderParams(gfx::FontRenderParamsQuery(), nullptr));
prefs->should_antialias_text = params->antialiasing;
prefs->use_subpixel_positioning = params->subpixel_positioning;
prefs->hinting = params->hinting;
prefs->use_autohinter = params->autohinter;
prefs->use_bitmaps = params->use_bitmaps;
prefs->subpixel_rendering = params->subpixel_rendering;
#endif
// Honor the system‘s cursor blink rate settings
if (auto interval = GetCursorBlinkInterval())
prefs->caret_blink_interval = *interval;
// Save the preferences in C++.
new WebContentsPreferences(web_contents(), options);
WebContentsPermissionHelper::CreateForWebContents(web_contents());
SecurityStateTabHelper::CreateForWebContents(web_contents());
InitZoomController(web_contents(), options);
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
extensions::AtomExtensionWebContentsObserver::CreateForWebContents(
web_contents());
#endif
registry_.AddInterface(base::BindRepeating(&WebContents::BindElectronBrowser,
base::Unretained(this)));
bindings_.set_connection_error_handler(base::BindRepeating(
&WebContents::OnElectronBrowserConnectionError, base::Unretained(this)));
AutofillDriverFactory::CreateForWebContents(web_contents());
web_contents()->SetUserAgentOverride(GetBrowserContext()->GetUserAgent(),
false);
if (IsGuest()) {
NativeWindow* owner_window = nullptr;
if (embedder_) {
// New WebContents‘s owner_window is the embedder‘s owner_window.
auto* relay =
NativeWindowRelay::FromWebContents(embedder_->web_contents());
if (relay)
owner_window = relay->GetNativeWindow();
}
if (owner_window)
SetOwnerWindow(owner_window);
}
Init(isolate);
AttachAsUserData(web_contents());
}
WebContents::~WebContents() {
// The destroy() is called.
if (managed_web_contents()) {
managed_web_contents()->GetView()->SetDelegate(nullptr);
RenderViewDeleted(web_contents()->GetRenderViewHost());
if (type_ == Type::BROWSER_WINDOW && owner_window()) {
// For BrowserWindow we should close the window and clean up everything
// before WebContents is destroyed.
for (ExtendedWebContentsObserver& observer : observers_)
observer.OnCloseContents();
// BrowserWindow destroys WebContents asynchronously, manually emit the
// destroyed event here.
WebContentsDestroyed();
} else if (Browser::Get()->is_shutting_down()) {
// Destroy WebContents directly when app is shutting down.
DestroyWebContents(false /* async */);
} else {
// Destroy WebContents asynchronously unless app is shutting down,
// because destroy() might be called inside WebContents‘s event handler.
DestroyWebContents(!IsGuest() /* async */);
// The WebContentsDestroyed will not be called automatically because we
// destroy the webContents in the next tick. So we have to manually
// call it here to make sure "destroyed" event is emitted.
WebContentsDestroyed();
}
}
}
void WebContents::DestroyWebContents(bool async) {
// This event is only for internal use, which is emitted when WebContents is
// being destroyed.
Emit("will-destroy");
ResetManagedWebContents(async);
}
bool WebContents::DidAddMessageToConsole(
content::WebContents* source,
blink::mojom::ConsoleMessageLevel level,
const base::string16& message,
int32_t line_no,
const base::string16& source_id) {
return Emit("console-message", static_cast(level), message, line_no,
source_id);
}
void WebContents::OnCreateWindow(
const GURL& target_url,
const content::Referrer& referrer,
const std::string& frame_name,
WindowOpenDisposition disposition,
const std::vector<:>string>& features,
const scoped_refptr<:resourcerequestbody>& body) {
if (type_ == Type::BROWSER_WINDOW || type_ == Type::OFF_SCREEN)
Emit("-new-window", target_url, frame_name, disposition, features, body,
referrer);
else
Emit("new-window", target_url, frame_name, disposition, features);
}
void WebContents::WebContentsCreated(content::WebContents* source_contents,
int opener_render_process_id,
int opener_render_frame_id,
const std::string& frame_name,
const GURL& target_url,
content::WebContents* new_contents) {
ChildWebContentsTracker::CreateForWebContents(new_contents);
auto* tracker = ChildWebContentsTracker::FromWebContents(new_contents);
tracker->url = target_url;
tracker->frame_name = frame_name;
}
void WebContents::AddNewContents(
content::WebContents* source,
std::unique_ptr<:webcontents> new_contents,
WindowOpenDisposition disposition,
const gfx::Rect& initial_rect,
bool user_gesture,
bool* was_blocked) {
auto* tracker = ChildWebContentsTracker::FromWebContents(new_contents.get());
DCHECK(tracker);
v8::Locker locker(isolate());
v8::HandleScope handle_scope(isolate());
auto api_web_contents =
CreateAndTake(isolate(), std::move(new_contents), Type::BROWSER_WINDOW);
if (Emit("-add-new-contents", api_web_contents, disposition, user_gesture,
initial_rect.x(), initial_rect.y(), initial_rect.width(),
initial_rect.height(), tracker->url, tracker->frame_name)) {
// TODO(zcbenz): Can we make this sync?
api_web_contents->DestroyWebContents(true /* async */);
}
}
content::WebContents* WebContents::OpenURLFromTab(
content::WebContents* source,
const content::OpenURLParams& params) {
if (params.disposition != WindowOpenDisposition::CURRENT_TAB) {
if (type_ == Type::BROWSER_WINDOW || type_ == Type::OFF_SCREEN)
Emit("-new-window", params.url, "", params.disposition);
else
Emit("new-window", params.url, "", params.disposition);
return nullptr;
}
// Give user a chance to cancel navigation.
if (Emit("will-navigate", params.url))
return nullptr;
// Don‘t load the URL if the web contents was marked as destroyed from a
// will-navigate event listener
if (IsDestroyed())
return nullptr;
return CommonWebContentsDelegate::OpenURLFromTab(source, params);
}
void WebContents::BeforeUnloadFired(content::WebContents* tab,
bool proceed,
bool* proceed_to_fire_unload) {
if (type_ == Type::BROWSER_WINDOW || type_ == Type::OFF_SCREEN)
*proceed_to_fire_unload = proceed;
else
*proceed_to_fire_unload = true;
}
void WebContents::SetContentsBounds(content::WebContents* source,
const gfx::Rect& pos) {
Emit("move", pos);
}
void WebContents::CloseContents(content::WebContents* source) {
Emit("close");
auto* autofill_driver_factory =
AutofillDriverFactory::FromWebContents(web_contents());
if (autofill_driver_factory) {
autofill_driver_factory->CloseAllPopups();
}
if (managed_web_contents())
managed_web_contents()->GetView()->SetDelegate(nullptr);
for (ExtendedWebContentsObserver& observer : observers_)
observer.OnCloseContents();
}
void WebContents::ActivateContents(content::WebContents* source) {
Emit("activate");
}
void WebContents::UpdateTargetURL(content::WebContents* source,
const GURL& url) {
Emit("update-target-url", url);
}
bool WebContents::HandleKeyboardEvent(
content::WebContents* source,
const content::NativeWebKeyboardEvent& event) {
if (type_ == Type::WEB_VIEW && embedder_) {
// Send the unhandled keyboard events back to the embedder.
return embedder_->HandleKeyboardEvent(source, event);
} else {
// Go to the default keyboard handling.
return CommonWebContentsDelegate::HandleKeyboardEvent(source, event);
}
}
content::KeyboardEventProcessingResult WebContents::PreHandleKeyboardEvent(
content::WebContents* source,
const content::NativeWebKeyboardEvent& event) {
if (event.GetType() == blink::WebInputEvent::Type::kRawKeyDown ||
event.GetType() == blink::WebInputEvent::Type::kKeyUp) {
bool prevent_default = Emit("before-input-event", event);
if (prevent_default) {
return content::KeyboardEventProcessingResult::HANDLED;
}
}
return content::KeyboardEventProcessingResult::NOT_HANDLED;
}
void WebContents::ContentsZoomChange(bool zoom_in) {
Emit("zoom-changed", zoom_in ? "in" : "out");
}
void WebContents::EnterFullscreenModeForTab(
content::WebContents* source,
const GURL& origin,
const blink::mojom::FullscreenOptions& options) {
auto* permission_helper =
WebContentsPermissionHelper::FromWebContents(source);
auto callback =
base::BindRepeating(&WebContents::OnEnterFullscreenModeForTab,
base::Unretained(this), source, origin, options);
permission_helper->RequestFullscreenPermission(callback);
}
void WebContents::OnEnterFullscreenModeForTab(
content::WebContents* source,
const