2012-04-03 03:34:16 +02:00
|
|
|
// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
|
|
|
|
// reserved. Use of this source code is governed by a BSD-style license that can
|
|
|
|
// be found in the LICENSE file.
|
|
|
|
|
2017-05-19 11:06:00 +02:00
|
|
|
#include "libcef/browser/frame_host_impl.h"
|
2019-05-11 00:14:48 +02:00
|
|
|
|
2012-04-03 03:34:16 +02:00
|
|
|
#include "include/cef_request.h"
|
|
|
|
#include "include/cef_stream.h"
|
|
|
|
#include "include/cef_v8.h"
|
2017-05-31 17:33:30 +02:00
|
|
|
#include "include/test/cef_test_helpers.h"
|
2020-09-18 00:24:08 +02:00
|
|
|
#include "libcef/browser/browser_host_base.h"
|
2019-05-11 00:14:48 +02:00
|
|
|
#include "libcef/browser/net_service/browser_urlrequest_impl.h"
|
2021-05-14 18:58:55 +02:00
|
|
|
#include "libcef/common/frame_util.h"
|
2021-04-15 01:28:22 +02:00
|
|
|
#include "libcef/common/net/url_util.h"
|
2019-05-24 22:23:43 +02:00
|
|
|
#include "libcef/common/process_message_impl.h"
|
2022-07-04 11:49:15 +02:00
|
|
|
#include "libcef/common/process_message_smr_impl.h"
|
2019-05-24 22:23:43 +02:00
|
|
|
#include "libcef/common/request_impl.h"
|
2021-05-14 18:58:55 +02:00
|
|
|
#include "libcef/common/string_util.h"
|
2019-05-11 00:14:48 +02:00
|
|
|
#include "libcef/common/task_runner_impl.h"
|
|
|
|
|
2020-10-08 21:54:42 +02:00
|
|
|
#include "content/browser/renderer_host/frame_tree_node.h"
|
2019-05-11 00:14:48 +02:00
|
|
|
#include "content/public/browser/render_process_host.h"
|
|
|
|
#include "content/public/browser/render_view_host.h"
|
2021-05-14 18:58:55 +02:00
|
|
|
#include "services/service_manager/public/cpp/interface_provider.h"
|
2012-04-03 03:34:16 +02:00
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
2021-05-14 18:58:55 +02:00
|
|
|
void StringVisitCallback(CefRefPtr<CefStringVisitor> visitor,
|
|
|
|
base::ReadOnlySharedMemoryRegion response) {
|
|
|
|
string_util::ExecuteWithScopedCefString(
|
|
|
|
std::move(response),
|
|
|
|
base::BindOnce([](CefRefPtr<CefStringVisitor> visitor,
|
|
|
|
const CefString& str) { visitor->Visit(str); },
|
|
|
|
visitor));
|
|
|
|
}
|
2017-05-17 11:29:28 +02:00
|
|
|
|
2021-05-14 18:58:55 +02:00
|
|
|
void ViewTextCallback(CefRefPtr<CefFrameHostImpl> frame,
|
|
|
|
base::ReadOnlySharedMemoryRegion response) {
|
|
|
|
if (auto browser = frame->GetBrowser()) {
|
|
|
|
string_util::ExecuteWithScopedCefString(
|
|
|
|
std::move(response),
|
|
|
|
base::BindOnce(
|
|
|
|
[](CefRefPtr<CefBrowser> browser, const CefString& str) {
|
|
|
|
static_cast<CefBrowserHostBase*>(browser.get())->ViewText(str);
|
|
|
|
},
|
|
|
|
browser));
|
2012-04-03 03:34:16 +02:00
|
|
|
}
|
2021-05-14 18:58:55 +02:00
|
|
|
}
|
2012-04-03 03:34:16 +02:00
|
|
|
|
2023-01-12 22:45:43 +01:00
|
|
|
using CefFrameHostImplCommand = void (CefFrameHostImpl::*)();
|
|
|
|
using WebContentsCommand = void (content::WebContents::*)();
|
|
|
|
|
|
|
|
void ExecWebContentsCommand(CefFrameHostImpl* fh,
|
|
|
|
CefFrameHostImplCommand fh_func,
|
|
|
|
WebContentsCommand wc_func,
|
|
|
|
const std::string& command) {
|
|
|
|
if (!CEF_CURRENTLY_ON_UIT()) {
|
|
|
|
CEF_POST_TASK(CEF_UIT, base::BindOnce(fh_func, fh));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
auto rfh = fh->GetRenderFrameHost();
|
|
|
|
if (rfh) {
|
|
|
|
auto web_contents = content::WebContents::FromRenderFrameHost(rfh);
|
|
|
|
if (web_contents) {
|
|
|
|
std::invoke(wc_func, web_contents);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fh->SendCommand(command);
|
|
|
|
}
|
|
|
|
|
|
|
|
#define EXEC_WEBCONTENTS_COMMAND(name) \
|
|
|
|
ExecWebContentsCommand(this, &CefFrameHostImpl::name, \
|
|
|
|
&content::WebContents::name, #name);
|
|
|
|
|
2012-04-03 03:34:16 +02:00
|
|
|
} // namespace
|
|
|
|
|
2019-05-24 22:23:43 +02:00
|
|
|
CefFrameHostImpl::CefFrameHostImpl(scoped_refptr<CefBrowserInfo> browser_info,
|
|
|
|
int64_t parent_frame_id)
|
2021-05-21 03:42:58 +02:00
|
|
|
: is_main_frame_(false),
|
2019-05-24 22:23:43 +02:00
|
|
|
frame_id_(kInvalidFrameId),
|
|
|
|
browser_info_(browser_info),
|
2014-09-24 17:38:11 +02:00
|
|
|
is_focused_(is_main_frame_), // The main frame always starts focused.
|
2019-05-24 22:23:43 +02:00
|
|
|
parent_frame_id_(parent_frame_id) {
|
|
|
|
#if DCHECK_IS_ON()
|
|
|
|
DCHECK(browser_info_);
|
|
|
|
if (is_main_frame_) {
|
|
|
|
DCHECK_EQ(parent_frame_id_, kInvalidFrameId);
|
|
|
|
} else {
|
|
|
|
DCHECK_GT(parent_frame_id_, 0);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
CefFrameHostImpl::CefFrameHostImpl(scoped_refptr<CefBrowserInfo> browser_info,
|
|
|
|
content::RenderFrameHost* render_frame_host)
|
|
|
|
: is_main_frame_(render_frame_host->GetParent() == nullptr),
|
2021-08-19 23:07:44 +02:00
|
|
|
frame_id_(frame_util::MakeFrameId(render_frame_host->GetGlobalId())),
|
2019-05-24 22:23:43 +02:00
|
|
|
browser_info_(browser_info),
|
|
|
|
is_focused_(is_main_frame_), // The main frame always starts focused.
|
2021-05-21 03:42:58 +02:00
|
|
|
url_(render_frame_host->GetLastCommittedURL().spec()),
|
|
|
|
name_(render_frame_host->GetFrameName()),
|
2021-08-19 23:07:44 +02:00
|
|
|
parent_frame_id_(
|
|
|
|
is_main_frame_ ? kInvalidFrameId
|
|
|
|
: frame_util::MakeFrameId(
|
|
|
|
render_frame_host->GetParent()->GetGlobalId())),
|
2021-05-21 03:42:58 +02:00
|
|
|
render_frame_host_(render_frame_host) {
|
2019-05-24 22:23:43 +02:00
|
|
|
DCHECK(browser_info_);
|
|
|
|
}
|
2012-04-03 03:34:16 +02:00
|
|
|
|
2021-05-21 03:42:58 +02:00
|
|
|
CefFrameHostImpl::~CefFrameHostImpl() {
|
2021-11-16 23:43:33 +01:00
|
|
|
// Should have been Detached if not temporary.
|
|
|
|
DCHECK(is_temporary() || !browser_info_);
|
2021-05-21 03:42:58 +02:00
|
|
|
DCHECK(!render_frame_host_);
|
2019-05-24 22:23:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool CefFrameHostImpl::IsValid() {
|
2020-09-18 00:24:08 +02:00
|
|
|
return !!GetBrowserHostBase();
|
2012-04-03 03:34:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void CefFrameHostImpl::Undo() {
|
2023-01-12 22:45:43 +01:00
|
|
|
EXEC_WEBCONTENTS_COMMAND(Undo);
|
2012-04-03 03:34:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void CefFrameHostImpl::Redo() {
|
2023-01-12 22:45:43 +01:00
|
|
|
EXEC_WEBCONTENTS_COMMAND(Redo);
|
2012-04-03 03:34:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void CefFrameHostImpl::Cut() {
|
2023-01-12 22:45:43 +01:00
|
|
|
EXEC_WEBCONTENTS_COMMAND(Cut);
|
2012-04-03 03:34:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void CefFrameHostImpl::Copy() {
|
2023-01-12 22:45:43 +01:00
|
|
|
EXEC_WEBCONTENTS_COMMAND(Copy);
|
2012-04-03 03:34:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void CefFrameHostImpl::Paste() {
|
2023-01-12 22:45:43 +01:00
|
|
|
EXEC_WEBCONTENTS_COMMAND(Paste);
|
2012-04-03 03:34:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void CefFrameHostImpl::Delete() {
|
2023-01-12 22:45:43 +01:00
|
|
|
EXEC_WEBCONTENTS_COMMAND(Delete);
|
2012-04-03 03:34:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void CefFrameHostImpl::SelectAll() {
|
2023-01-12 22:45:43 +01:00
|
|
|
EXEC_WEBCONTENTS_COMMAND(SelectAll);
|
2012-04-03 03:34:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void CefFrameHostImpl::ViewSource() {
|
2021-05-14 18:58:55 +02:00
|
|
|
SendCommandWithResponse(
|
|
|
|
"GetSource",
|
|
|
|
base::BindOnce(&ViewTextCallback, CefRefPtr<CefFrameHostImpl>(this)));
|
2012-04-03 03:34:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void CefFrameHostImpl::GetSource(CefRefPtr<CefStringVisitor> visitor) {
|
2021-05-14 18:58:55 +02:00
|
|
|
SendCommandWithResponse("GetSource",
|
|
|
|
base::BindOnce(&StringVisitCallback, visitor));
|
2012-04-03 03:34:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void CefFrameHostImpl::GetText(CefRefPtr<CefStringVisitor> visitor) {
|
2021-05-14 18:58:55 +02:00
|
|
|
SendCommandWithResponse("GetText",
|
|
|
|
base::BindOnce(&StringVisitCallback, visitor));
|
2012-04-03 03:34:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void CefFrameHostImpl::LoadRequest(CefRefPtr<CefRequest> request) {
|
2021-05-14 18:58:55 +02:00
|
|
|
auto params = cef::mojom::RequestParams::New();
|
2019-05-24 22:23:43 +02:00
|
|
|
static_cast<CefRequestImpl*>(request.get())->Get(params);
|
2021-05-14 18:58:55 +02:00
|
|
|
LoadRequest(std::move(params));
|
2012-04-03 03:34:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void CefFrameHostImpl::LoadURL(const CefString& url) {
|
2020-02-14 22:58:58 +01:00
|
|
|
LoadURLWithExtras(url, content::Referrer(), kPageTransitionExplicit,
|
2019-05-24 22:23:43 +02:00
|
|
|
std::string());
|
2012-04-03 03:34:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void CefFrameHostImpl::ExecuteJavaScript(const CefString& jsCode,
|
|
|
|
const CefString& scriptUrl,
|
|
|
|
int startLine) {
|
2012-10-18 00:45:49 +02:00
|
|
|
SendJavaScript(jsCode, scriptUrl, startLine);
|
2012-04-03 03:34:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool CefFrameHostImpl::IsMain() {
|
|
|
|
return is_main_frame_;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CefFrameHostImpl::IsFocused() {
|
|
|
|
base::AutoLock lock_scope(state_lock_);
|
|
|
|
return is_focused_;
|
|
|
|
}
|
|
|
|
|
|
|
|
CefString CefFrameHostImpl::GetName() {
|
|
|
|
base::AutoLock lock_scope(state_lock_);
|
|
|
|
return name_;
|
|
|
|
}
|
|
|
|
|
2023-06-01 16:06:15 +02:00
|
|
|
int64_t CefFrameHostImpl::GetIdentifier() {
|
2019-05-24 22:23:43 +02:00
|
|
|
base::AutoLock lock_scope(state_lock_);
|
2012-04-03 03:34:16 +02:00
|
|
|
return frame_id_;
|
|
|
|
}
|
|
|
|
|
|
|
|
CefRefPtr<CefFrame> CefFrameHostImpl::GetParent() {
|
2023-06-01 16:06:15 +02:00
|
|
|
int64_t parent_frame_id;
|
2013-05-31 17:34:53 +02:00
|
|
|
|
|
|
|
{
|
|
|
|
base::AutoLock lock_scope(state_lock_);
|
2023-01-02 23:59:03 +01:00
|
|
|
if (is_main_frame_ || parent_frame_id_ == kInvalidFrameId) {
|
2019-05-24 22:23:43 +02:00
|
|
|
return nullptr;
|
2023-01-02 23:59:03 +01:00
|
|
|
}
|
2013-05-31 17:34:53 +02:00
|
|
|
parent_frame_id = parent_frame_id_;
|
|
|
|
}
|
2012-04-03 03:34:16 +02:00
|
|
|
|
2020-09-18 00:24:08 +02:00
|
|
|
auto browser = GetBrowserHostBase();
|
2023-01-02 23:59:03 +01:00
|
|
|
if (browser) {
|
2013-05-31 17:34:53 +02:00
|
|
|
return browser->GetFrame(parent_frame_id);
|
2023-01-02 23:59:03 +01:00
|
|
|
}
|
2012-04-03 03:34:16 +02:00
|
|
|
|
2019-05-24 22:23:43 +02:00
|
|
|
return nullptr;
|
2012-04-03 03:34:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
CefString CefFrameHostImpl::GetURL() {
|
|
|
|
base::AutoLock lock_scope(state_lock_);
|
|
|
|
return url_;
|
|
|
|
}
|
|
|
|
|
|
|
|
CefRefPtr<CefBrowser> CefFrameHostImpl::GetBrowser() {
|
2020-09-18 00:24:08 +02:00
|
|
|
return GetBrowserHostBase().get();
|
2012-04-03 03:34:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
CefRefPtr<CefV8Context> CefFrameHostImpl::GetV8Context() {
|
2023-05-08 17:07:57 +02:00
|
|
|
DCHECK(false) << "GetV8Context cannot be called from the browser process";
|
2019-05-24 22:23:43 +02:00
|
|
|
return nullptr;
|
2012-04-03 03:34:16 +02:00
|
|
|
}
|
|
|
|
|
2012-04-27 23:19:06 +02:00
|
|
|
void CefFrameHostImpl::VisitDOM(CefRefPtr<CefDOMVisitor> visitor) {
|
2023-05-08 17:07:57 +02:00
|
|
|
DCHECK(false) << "VisitDOM cannot be called from the browser process";
|
2012-04-27 23:19:06 +02:00
|
|
|
}
|
|
|
|
|
2019-05-11 00:14:48 +02:00
|
|
|
CefRefPtr<CefURLRequest> CefFrameHostImpl::CreateURLRequest(
|
|
|
|
CefRefPtr<CefRequest> request,
|
|
|
|
CefRefPtr<CefURLRequestClient> client) {
|
2023-01-02 23:59:03 +01:00
|
|
|
if (!request || !client) {
|
2019-05-24 22:23:43 +02:00
|
|
|
return nullptr;
|
2023-01-02 23:59:03 +01:00
|
|
|
}
|
2019-05-11 00:14:48 +02:00
|
|
|
|
|
|
|
if (!CefTaskRunnerImpl::GetCurrentTaskRunner()) {
|
2023-05-08 17:07:57 +02:00
|
|
|
DCHECK(false) << "called on invalid thread";
|
2019-05-24 22:23:43 +02:00
|
|
|
return nullptr;
|
2019-05-11 00:14:48 +02:00
|
|
|
}
|
|
|
|
|
2020-09-18 00:24:08 +02:00
|
|
|
auto browser = GetBrowserHostBase();
|
2023-01-02 23:59:03 +01:00
|
|
|
if (!browser) {
|
2019-05-24 22:23:43 +02:00
|
|
|
return nullptr;
|
2023-01-02 23:59:03 +01:00
|
|
|
}
|
2019-05-11 00:14:48 +02:00
|
|
|
|
|
|
|
auto request_context = browser->request_context();
|
|
|
|
|
2019-07-29 23:27:12 +02:00
|
|
|
CefRefPtr<CefBrowserURLRequest> impl =
|
|
|
|
new CefBrowserURLRequest(this, request, client, request_context);
|
2023-01-02 23:59:03 +01:00
|
|
|
if (impl->Start()) {
|
2019-07-29 23:27:12 +02:00
|
|
|
return impl.get();
|
2023-01-02 23:59:03 +01:00
|
|
|
}
|
2019-05-24 22:23:43 +02:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CefFrameHostImpl::SendProcessMessage(
|
|
|
|
CefProcessId target_process,
|
|
|
|
CefRefPtr<CefProcessMessage> message) {
|
|
|
|
DCHECK_EQ(PID_RENDERER, target_process);
|
2021-05-14 18:58:55 +02:00
|
|
|
DCHECK(message && message->IsValid());
|
2023-01-02 23:59:03 +01:00
|
|
|
if (!message || !message->IsValid()) {
|
2019-05-24 22:23:43 +02:00
|
|
|
return;
|
2023-01-02 23:59:03 +01:00
|
|
|
}
|
2019-05-24 22:23:43 +02:00
|
|
|
|
2022-07-04 11:49:15 +02:00
|
|
|
if (message->GetArgumentList() != nullptr) {
|
|
|
|
// Invalidate the message object immediately by taking the argument list.
|
|
|
|
auto argument_list =
|
|
|
|
static_cast<CefProcessMessageImpl*>(message.get())->TakeArgumentList();
|
|
|
|
SendToRenderFrame(
|
|
|
|
__FUNCTION__,
|
|
|
|
base::BindOnce(
|
2023-01-30 22:42:40 +01:00
|
|
|
[](const CefString& name, base::Value::List argument_list,
|
2022-07-04 11:49:15 +02:00
|
|
|
const RenderFrameType& render_frame) {
|
|
|
|
render_frame->SendMessage(name, std::move(argument_list));
|
|
|
|
},
|
|
|
|
message->GetName(), std::move(argument_list)));
|
|
|
|
} else {
|
|
|
|
auto region =
|
|
|
|
static_cast<CefProcessMessageSMRImpl*>(message.get())->TakeRegion();
|
|
|
|
SendToRenderFrame(
|
|
|
|
__FUNCTION__,
|
|
|
|
base::BindOnce(
|
|
|
|
[](const CefString& name, base::ReadOnlySharedMemoryRegion region,
|
|
|
|
const RenderFrameType& render_frame) {
|
|
|
|
render_frame->SendSharedMemoryRegion(name, std::move(region));
|
|
|
|
},
|
|
|
|
message->GetName(), std::move(region)));
|
|
|
|
}
|
2019-05-24 22:23:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void CefFrameHostImpl::SetFocused(bool focused) {
|
|
|
|
base::AutoLock lock_scope(state_lock_);
|
|
|
|
is_focused_ = focused;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CefFrameHostImpl::RefreshAttributes() {
|
|
|
|
CEF_REQUIRE_UIT();
|
|
|
|
|
|
|
|
base::AutoLock lock_scope(state_lock_);
|
2023-01-02 23:59:03 +01:00
|
|
|
if (!render_frame_host_) {
|
2019-05-24 22:23:43 +02:00
|
|
|
return;
|
2023-01-02 23:59:03 +01:00
|
|
|
}
|
2019-05-24 22:23:43 +02:00
|
|
|
url_ = render_frame_host_->GetLastCommittedURL().spec();
|
|
|
|
|
|
|
|
// Use the assigned name if it is non-empty. This represents the name property
|
|
|
|
// on the frame DOM element. If the assigned name is empty, revert to the
|
|
|
|
// internal unique name. This matches the logic in render_frame_util::GetName.
|
|
|
|
name_ = render_frame_host_->GetFrameName();
|
|
|
|
if (name_.empty()) {
|
|
|
|
const auto node = content::FrameTreeNode::GloballyFindByID(
|
|
|
|
render_frame_host_->GetFrameTreeNodeId());
|
|
|
|
if (node) {
|
|
|
|
name_ = node->unique_name();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-19 23:07:44 +02:00
|
|
|
if (!is_main_frame_) {
|
|
|
|
parent_frame_id_ =
|
|
|
|
frame_util::MakeFrameId(render_frame_host_->GetParent()->GetGlobalId());
|
|
|
|
}
|
2019-05-24 22:23:43 +02:00
|
|
|
}
|
|
|
|
|
2023-01-06 19:33:32 +01:00
|
|
|
void CefFrameHostImpl::NotifyMoveOrResizeStarted() {
|
|
|
|
SendToRenderFrame(__FUNCTION__,
|
|
|
|
base::BindOnce([](const RenderFrameType& render_frame) {
|
|
|
|
render_frame->MoveOrResizeStarted();
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
|
2021-05-14 18:58:55 +02:00
|
|
|
void CefFrameHostImpl::LoadRequest(cef::mojom::RequestParamsPtr params) {
|
2023-01-02 23:59:03 +01:00
|
|
|
if (!url_util::FixupGURL(params->url)) {
|
2019-05-24 22:23:43 +02:00
|
|
|
return;
|
2023-01-02 23:59:03 +01:00
|
|
|
}
|
2019-05-24 22:23:43 +02:00
|
|
|
|
2021-05-21 03:42:58 +02:00
|
|
|
SendToRenderFrame(__FUNCTION__,
|
|
|
|
base::BindOnce(
|
|
|
|
[](cef::mojom::RequestParamsPtr params,
|
|
|
|
const RenderFrameType& render_frame) {
|
|
|
|
render_frame->LoadRequest(std::move(params));
|
|
|
|
},
|
|
|
|
std::move(params)));
|
2019-05-24 22:23:43 +02:00
|
|
|
|
2020-09-18 00:24:08 +02:00
|
|
|
auto browser = GetBrowserHostBase();
|
2023-01-02 23:59:03 +01:00
|
|
|
if (browser) {
|
2019-05-24 22:23:43 +02:00
|
|
|
browser->OnSetFocus(FOCUS_SOURCE_NAVIGATION);
|
2023-01-02 23:59:03 +01:00
|
|
|
}
|
2019-05-24 22:23:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void CefFrameHostImpl::LoadURLWithExtras(const std::string& url,
|
|
|
|
const content::Referrer& referrer,
|
|
|
|
ui::PageTransition transition,
|
|
|
|
const std::string& extra_headers) {
|
|
|
|
// Only known frame ids or kMainFrameId are supported.
|
|
|
|
const auto frame_id = GetFrameId();
|
2023-01-02 23:59:03 +01:00
|
|
|
if (frame_id < CefFrameHostImpl::kMainFrameId) {
|
2019-05-24 22:23:43 +02:00
|
|
|
return;
|
2023-01-02 23:59:03 +01:00
|
|
|
}
|
2019-05-24 22:23:43 +02:00
|
|
|
|
2021-05-14 18:58:55 +02:00
|
|
|
// Any necessary fixup will occur in LoadRequest.
|
|
|
|
GURL gurl = url_util::MakeGURL(url, /*fixup=*/false);
|
2020-09-25 03:40:47 +02:00
|
|
|
|
2019-05-24 22:23:43 +02:00
|
|
|
if (frame_id == CefFrameHostImpl::kMainFrameId) {
|
|
|
|
// Load via the browser using NavigationController.
|
2020-09-18 00:24:08 +02:00
|
|
|
auto browser = GetBrowserHostBase();
|
2019-05-24 22:23:43 +02:00
|
|
|
if (browser) {
|
2020-09-25 03:40:47 +02:00
|
|
|
content::OpenURLParams params(
|
|
|
|
gurl, referrer, WindowOpenDisposition::CURRENT_TAB, transition,
|
|
|
|
/*is_renderer_initiated=*/false);
|
|
|
|
params.extra_headers = extra_headers;
|
|
|
|
|
|
|
|
browser->LoadMainFrameURL(params);
|
2019-05-24 22:23:43 +02:00
|
|
|
}
|
|
|
|
} else {
|
2021-05-14 18:58:55 +02:00
|
|
|
auto params = cef::mojom::RequestParams::New();
|
|
|
|
params->url = gurl;
|
|
|
|
params->referrer =
|
|
|
|
blink::mojom::Referrer::New(referrer.url, referrer.policy);
|
|
|
|
params->headers = extra_headers;
|
|
|
|
LoadRequest(std::move(params));
|
2019-05-24 22:23:43 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-14 18:58:55 +02:00
|
|
|
void CefFrameHostImpl::SendCommand(const std::string& command) {
|
2019-05-24 22:23:43 +02:00
|
|
|
DCHECK(!command.empty());
|
2021-05-21 03:42:58 +02:00
|
|
|
SendToRenderFrame(__FUNCTION__, base::BindOnce(
|
|
|
|
[](const std::string& command,
|
|
|
|
const RenderFrameType& render_frame) {
|
|
|
|
render_frame->SendCommand(command);
|
|
|
|
},
|
|
|
|
command));
|
2019-05-11 00:14:48 +02:00
|
|
|
}
|
|
|
|
|
2021-05-14 18:58:55 +02:00
|
|
|
void CefFrameHostImpl::SendCommandWithResponse(
|
|
|
|
const std::string& command,
|
|
|
|
cef::mojom::RenderFrame::SendCommandWithResponseCallback
|
|
|
|
response_callback) {
|
|
|
|
DCHECK(!command.empty());
|
2021-05-21 03:42:58 +02:00
|
|
|
SendToRenderFrame(
|
|
|
|
__FUNCTION__,
|
|
|
|
base::BindOnce(
|
|
|
|
[](const std::string& command,
|
|
|
|
cef::mojom::RenderFrame::SendCommandWithResponseCallback
|
|
|
|
response_callback,
|
|
|
|
const RenderFrameType& render_frame) {
|
|
|
|
render_frame->SendCommandWithResponse(command,
|
|
|
|
std::move(response_callback));
|
|
|
|
},
|
|
|
|
command, std::move(response_callback)));
|
2021-05-14 18:58:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void CefFrameHostImpl::SendJavaScript(const std::u16string& jsCode,
|
2017-05-17 11:29:28 +02:00
|
|
|
const std::string& scriptUrl,
|
|
|
|
int startLine) {
|
2023-01-02 23:59:03 +01:00
|
|
|
if (jsCode.empty()) {
|
2012-10-18 00:45:49 +02:00
|
|
|
return;
|
2023-01-02 23:59:03 +01:00
|
|
|
}
|
2016-10-17 20:14:44 +02:00
|
|
|
if (startLine <= 0) {
|
|
|
|
// A value of 0 is v8::Message::kNoLineNumberInfo in V8. There is code in
|
|
|
|
// V8 that will assert on that value (e.g. V8StackTraceImpl::Frame::Frame
|
|
|
|
// if a JS exception is thrown) so make sure |startLine| > 0.
|
|
|
|
startLine = 1;
|
|
|
|
}
|
2012-10-18 00:45:49 +02:00
|
|
|
|
2021-05-21 03:42:58 +02:00
|
|
|
SendToRenderFrame(
|
|
|
|
__FUNCTION__,
|
|
|
|
base::BindOnce(
|
|
|
|
[](const std::u16string& jsCode, const std::string& scriptUrl,
|
|
|
|
int startLine, const RenderFrameType& render_frame) {
|
|
|
|
render_frame->SendJavaScript(jsCode, scriptUrl, startLine);
|
|
|
|
},
|
|
|
|
jsCode, scriptUrl, startLine));
|
2019-05-24 22:23:43 +02:00
|
|
|
}
|
2013-05-31 17:34:53 +02:00
|
|
|
|
2019-05-24 22:23:43 +02:00
|
|
|
void CefFrameHostImpl::MaybeSendDidStopLoading() {
|
|
|
|
auto rfh = GetRenderFrameHost();
|
2023-01-02 23:59:03 +01:00
|
|
|
if (!rfh) {
|
2019-05-24 22:23:43 +02:00
|
|
|
return;
|
2023-01-02 23:59:03 +01:00
|
|
|
}
|
2019-05-24 22:23:43 +02:00
|
|
|
|
|
|
|
// We only want to notify for the highest-level LocalFrame in this frame's
|
|
|
|
// renderer process subtree. If this frame has a parent in the same process
|
|
|
|
// then the notification will be sent via the parent instead.
|
|
|
|
auto rfh_parent = rfh->GetParent();
|
|
|
|
if (rfh_parent && rfh_parent->GetProcess() == rfh->GetProcess()) {
|
|
|
|
return;
|
2012-10-18 00:45:49 +02:00
|
|
|
}
|
2013-05-31 17:34:53 +02:00
|
|
|
|
2021-05-21 03:42:58 +02:00
|
|
|
SendToRenderFrame(__FUNCTION__,
|
|
|
|
base::BindOnce([](const RenderFrameType& render_frame) {
|
|
|
|
render_frame->DidStopLoading();
|
|
|
|
}));
|
2012-10-18 00:45:49 +02:00
|
|
|
}
|
|
|
|
|
2017-05-31 17:33:30 +02:00
|
|
|
void CefFrameHostImpl::ExecuteJavaScriptWithUserGestureForTests(
|
|
|
|
const CefString& javascript) {
|
2019-05-11 00:14:48 +02:00
|
|
|
if (!CEF_CURRENTLY_ON_UIT()) {
|
|
|
|
CEF_POST_TASK(
|
|
|
|
CEF_UIT,
|
|
|
|
base::BindOnce(
|
|
|
|
&CefFrameHostImpl::ExecuteJavaScriptWithUserGestureForTests, this,
|
|
|
|
javascript));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
content::RenderFrameHost* rfh = GetRenderFrameHost();
|
2022-04-27 21:24:24 +02:00
|
|
|
if (rfh) {
|
|
|
|
rfh->ExecuteJavaScriptWithUserGestureForTests(javascript,
|
|
|
|
base::NullCallback());
|
|
|
|
}
|
2019-05-11 00:14:48 +02:00
|
|
|
}
|
|
|
|
|
2019-05-24 22:23:43 +02:00
|
|
|
content::RenderFrameHost* CefFrameHostImpl::GetRenderFrameHost() const {
|
2019-05-11 00:14:48 +02:00
|
|
|
CEF_REQUIRE_UIT();
|
2019-05-24 22:23:43 +02:00
|
|
|
return render_frame_host_;
|
|
|
|
}
|
2019-05-11 00:14:48 +02:00
|
|
|
|
2022-08-16 19:10:43 +02:00
|
|
|
bool CefFrameHostImpl::Detach(DetachReason reason) {
|
2019-05-24 22:23:43 +02:00
|
|
|
CEF_REQUIRE_UIT();
|
2017-05-31 17:33:30 +02:00
|
|
|
|
2022-08-16 19:10:43 +02:00
|
|
|
if (VLOG_IS_ON(1)) {
|
|
|
|
std::string reason_str;
|
|
|
|
switch (reason) {
|
|
|
|
case DetachReason::RENDER_FRAME_DELETED:
|
|
|
|
reason_str = "RENDER_FRAME_DELETED";
|
|
|
|
break;
|
|
|
|
case DetachReason::NEW_MAIN_FRAME:
|
|
|
|
reason_str = "NEW_MAIN_FRAME";
|
|
|
|
break;
|
|
|
|
case DetachReason::BROWSER_DESTROYED:
|
|
|
|
reason_str = "BROWSER_DESTROYED";
|
|
|
|
break;
|
|
|
|
};
|
|
|
|
|
|
|
|
VLOG(1) << GetDebugString() << " detached (reason=" << reason_str
|
|
|
|
<< ", is_connected=" << render_frame_.is_bound() << ")";
|
|
|
|
}
|
|
|
|
|
2021-07-25 19:31:39 +02:00
|
|
|
// May be called multiple times (e.g. from CefBrowserInfo SetMainFrame and
|
|
|
|
// RemoveFrame).
|
|
|
|
bool first_detach = false;
|
|
|
|
|
2021-05-21 03:42:58 +02:00
|
|
|
// Should not be called for temporary frames.
|
|
|
|
DCHECK(!is_temporary());
|
|
|
|
|
2017-05-31 17:33:30 +02:00
|
|
|
{
|
|
|
|
base::AutoLock lock_scope(state_lock_);
|
2021-07-25 19:31:39 +02:00
|
|
|
if (browser_info_) {
|
|
|
|
first_detach = true;
|
|
|
|
browser_info_ = nullptr;
|
|
|
|
}
|
2017-05-31 17:33:30 +02:00
|
|
|
}
|
|
|
|
|
2019-05-24 22:23:43 +02:00
|
|
|
// In case we never attached, clean up.
|
2022-02-10 22:52:36 +01:00
|
|
|
while (!queued_renderer_actions_.empty()) {
|
|
|
|
queued_renderer_actions_.pop();
|
2019-05-24 22:23:43 +02:00
|
|
|
}
|
2019-05-11 00:14:48 +02:00
|
|
|
|
2022-08-16 20:47:47 +02:00
|
|
|
if (render_frame_.is_bound()) {
|
|
|
|
render_frame_->FrameDetached();
|
|
|
|
}
|
|
|
|
|
2021-05-14 18:58:55 +02:00
|
|
|
render_frame_.reset();
|
2019-05-24 22:23:43 +02:00
|
|
|
render_frame_host_ = nullptr;
|
2021-07-25 19:31:39 +02:00
|
|
|
|
|
|
|
return first_detach;
|
2019-05-24 22:23:43 +02:00
|
|
|
}
|
2019-05-11 00:14:48 +02:00
|
|
|
|
2021-09-15 13:40:08 +02:00
|
|
|
void CefFrameHostImpl::MaybeReAttach(
|
|
|
|
scoped_refptr<CefBrowserInfo> browser_info,
|
|
|
|
content::RenderFrameHost* render_frame_host) {
|
|
|
|
CEF_REQUIRE_UIT();
|
2022-02-10 22:52:36 +01:00
|
|
|
if (render_frame_.is_bound() && render_frame_host_ == render_frame_host) {
|
2021-09-15 13:40:08 +02:00
|
|
|
// Nothing to do here.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// We expect that Detach() was called previously.
|
|
|
|
CHECK(!is_temporary());
|
2022-02-10 22:52:36 +01:00
|
|
|
CHECK(!render_frame_.is_bound());
|
2021-09-15 13:40:08 +02:00
|
|
|
CHECK(!render_frame_host_);
|
|
|
|
|
|
|
|
// The RFH may change but the GlobalId should remain the same.
|
|
|
|
CHECK_EQ(frame_id_,
|
|
|
|
frame_util::MakeFrameId(render_frame_host->GetGlobalId()));
|
|
|
|
|
|
|
|
{
|
|
|
|
base::AutoLock lock_scope(state_lock_);
|
|
|
|
browser_info_ = browser_info;
|
|
|
|
}
|
|
|
|
|
|
|
|
render_frame_host_ = render_frame_host;
|
|
|
|
RefreshAttributes();
|
|
|
|
|
2022-02-10 22:52:36 +01:00
|
|
|
// We expect a reconnect to be triggered via FrameAttached().
|
2021-09-15 13:40:08 +02:00
|
|
|
}
|
|
|
|
|
2019-05-24 22:23:43 +02:00
|
|
|
// kMainFrameId must be -1 to align with renderer expectations.
|
|
|
|
const int64_t CefFrameHostImpl::kMainFrameId = -1;
|
|
|
|
const int64_t CefFrameHostImpl::kFocusedFrameId = -2;
|
|
|
|
const int64_t CefFrameHostImpl::kUnspecifiedFrameId = -3;
|
|
|
|
const int64_t CefFrameHostImpl::kInvalidFrameId = -4;
|
|
|
|
|
2020-02-14 22:58:58 +01:00
|
|
|
// This equates to (TT_EXPLICIT | TT_DIRECT_LOAD_FLAG).
|
|
|
|
const ui::PageTransition CefFrameHostImpl::kPageTransitionExplicit =
|
|
|
|
static_cast<ui::PageTransition>(ui::PAGE_TRANSITION_TYPED |
|
|
|
|
ui::PAGE_TRANSITION_FROM_ADDRESS_BAR);
|
|
|
|
|
2023-06-01 16:06:15 +02:00
|
|
|
int64_t CefFrameHostImpl::GetFrameId() const {
|
2012-04-03 03:34:16 +02:00
|
|
|
base::AutoLock lock_scope(state_lock_);
|
2019-05-24 22:23:43 +02:00
|
|
|
return is_main_frame_ ? kMainFrameId : frame_id_;
|
2012-04-03 03:34:16 +02:00
|
|
|
}
|
2013-05-31 17:34:53 +02:00
|
|
|
|
2021-05-21 03:42:58 +02:00
|
|
|
scoped_refptr<CefBrowserInfo> CefFrameHostImpl::GetBrowserInfo() const {
|
2019-05-24 22:23:43 +02:00
|
|
|
base::AutoLock lock_scope(state_lock_);
|
2021-05-21 03:42:58 +02:00
|
|
|
return browser_info_;
|
|
|
|
}
|
|
|
|
|
|
|
|
CefRefPtr<CefBrowserHostBase> CefFrameHostImpl::GetBrowserHostBase() const {
|
2023-01-02 23:59:03 +01:00
|
|
|
if (auto browser_info = GetBrowserInfo()) {
|
2021-05-21 03:42:58 +02:00
|
|
|
return browser_info->browser();
|
2023-01-02 23:59:03 +01:00
|
|
|
}
|
2019-05-24 22:23:43 +02:00
|
|
|
return nullptr;
|
|
|
|
}
|
2013-05-31 17:34:53 +02:00
|
|
|
|
2021-05-21 03:42:58 +02:00
|
|
|
void CefFrameHostImpl::SendToRenderFrame(const std::string& function_name,
|
|
|
|
RenderFrameAction action) {
|
2021-05-14 18:58:55 +02:00
|
|
|
if (!CEF_CURRENTLY_ON_UIT()) {
|
2021-05-21 03:42:58 +02:00
|
|
|
CEF_POST_TASK(CEF_UIT,
|
|
|
|
base::BindOnce(&CefFrameHostImpl::SendToRenderFrame, this,
|
|
|
|
function_name, std::move(action)));
|
2019-05-24 22:23:43 +02:00
|
|
|
return;
|
2021-05-14 18:58:55 +02:00
|
|
|
}
|
2019-05-24 22:23:43 +02:00
|
|
|
|
2021-05-21 03:42:58 +02:00
|
|
|
if (is_temporary()) {
|
|
|
|
LOG(WARNING) << function_name
|
|
|
|
<< " sent to temporary subframe will be ignored.";
|
|
|
|
return;
|
|
|
|
} else if (!render_frame_host_) {
|
|
|
|
// We've been detached.
|
2022-08-16 19:10:43 +02:00
|
|
|
LOG(WARNING) << function_name << " sent to detached " << GetDebugString()
|
2021-05-21 03:42:58 +02:00
|
|
|
<< " will be ignored";
|
2019-05-24 22:23:43 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-02-10 22:52:36 +01:00
|
|
|
if (!render_frame_.is_bound()) {
|
2021-05-14 18:58:55 +02:00
|
|
|
// Queue actions until we're notified by the renderer that it's ready to
|
|
|
|
// handle them.
|
2022-02-10 22:52:36 +01:00
|
|
|
queued_renderer_actions_.push(
|
|
|
|
std::make_pair(function_name, std::move(action)));
|
2021-05-14 18:58:55 +02:00
|
|
|
return;
|
|
|
|
}
|
2019-05-24 22:23:43 +02:00
|
|
|
|
2022-02-10 22:52:36 +01:00
|
|
|
std::move(action).Run(render_frame_);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CefFrameHostImpl::OnRenderFrameDisconnect() {
|
|
|
|
CEF_REQUIRE_UIT();
|
2019-05-24 22:23:43 +02:00
|
|
|
|
2022-02-10 22:52:36 +01:00
|
|
|
// Reconnect, if any, will be triggered via FrameAttached().
|
|
|
|
render_frame_.reset();
|
2021-05-14 18:58:55 +02:00
|
|
|
}
|
2019-05-24 22:23:43 +02:00
|
|
|
|
2021-05-14 18:58:55 +02:00
|
|
|
void CefFrameHostImpl::SendMessage(const std::string& name,
|
2023-01-30 22:42:40 +01:00
|
|
|
base::Value::List arguments) {
|
2021-05-14 18:58:55 +02:00
|
|
|
if (auto browser = GetBrowserHostBase()) {
|
|
|
|
if (auto client = browser->GetClient()) {
|
2023-01-30 22:42:40 +01:00
|
|
|
CefRefPtr<CefProcessMessageImpl> message(
|
|
|
|
new CefProcessMessageImpl(name, std::move(arguments),
|
|
|
|
/*read_only=*/true));
|
2021-05-14 18:58:55 +02:00
|
|
|
browser->GetClient()->OnProcessMessageReceived(
|
2019-05-24 22:23:43 +02:00
|
|
|
browser.get(), this, PID_RENDERER, message.get());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-04 11:49:15 +02:00
|
|
|
void CefFrameHostImpl::SendSharedMemoryRegion(
|
|
|
|
const std::string& name,
|
|
|
|
base::ReadOnlySharedMemoryRegion region) {
|
|
|
|
if (auto browser = GetBrowserHostBase()) {
|
|
|
|
if (auto client = browser->GetClient()) {
|
|
|
|
CefRefPtr<CefProcessMessage> message(
|
|
|
|
new CefProcessMessageSMRImpl(name, std::move(region)));
|
|
|
|
browser->GetClient()->OnProcessMessageReceived(browser.get(), this,
|
|
|
|
PID_RENDERER, message);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-10 22:52:36 +01:00
|
|
|
void CefFrameHostImpl::FrameAttached(
|
|
|
|
mojo::PendingRemote<cef::mojom::RenderFrame> render_frame_remote,
|
|
|
|
bool reattached) {
|
2021-05-21 03:42:58 +02:00
|
|
|
CEF_REQUIRE_UIT();
|
2022-02-10 22:52:36 +01:00
|
|
|
CHECK(render_frame_remote);
|
2021-05-21 03:42:58 +02:00
|
|
|
|
2021-06-09 22:54:50 +02:00
|
|
|
auto browser_info = GetBrowserInfo();
|
|
|
|
if (!browser_info) {
|
|
|
|
// Already Detached.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-08-16 19:10:43 +02:00
|
|
|
VLOG(1) << GetDebugString() << " " << (reattached ? "re" : "") << "connected";
|
2019-05-24 22:23:43 +02:00
|
|
|
|
2022-02-10 22:52:36 +01:00
|
|
|
render_frame_.Bind(std::move(render_frame_remote));
|
|
|
|
render_frame_.set_disconnect_handler(
|
|
|
|
base::BindOnce(&CefFrameHostImpl::OnRenderFrameDisconnect, this));
|
2021-05-21 03:42:58 +02:00
|
|
|
|
2022-02-10 22:52:36 +01:00
|
|
|
// Notify the renderer process that it can start sending messages.
|
|
|
|
render_frame_->FrameAttachedAck();
|
|
|
|
|
|
|
|
while (!queued_renderer_actions_.empty()) {
|
|
|
|
std::move(queued_renderer_actions_.front().second).Run(render_frame_);
|
|
|
|
queued_renderer_actions_.pop();
|
2021-05-14 18:58:55 +02:00
|
|
|
}
|
2022-02-10 22:52:36 +01:00
|
|
|
|
|
|
|
browser_info->MaybeExecuteFrameNotification(base::BindOnce(
|
|
|
|
[](CefRefPtr<CefFrameHostImpl> self, bool reattached,
|
|
|
|
CefRefPtr<CefFrameHandler> handler) {
|
|
|
|
if (auto browser = self->GetBrowserHostBase()) {
|
|
|
|
handler->OnFrameAttached(browser, self, reattached);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
CefRefPtr<CefFrameHostImpl>(this), reattached));
|
2019-05-24 22:23:43 +02:00
|
|
|
}
|
|
|
|
|
2021-05-14 18:58:55 +02:00
|
|
|
void CefFrameHostImpl::UpdateDraggableRegions(
|
2021-06-04 03:34:56 +02:00
|
|
|
absl::optional<std::vector<cef::mojom::DraggableRegionEntryPtr>> regions) {
|
2021-05-14 18:58:55 +02:00
|
|
|
auto browser = GetBrowserHostBase();
|
2023-01-02 23:59:03 +01:00
|
|
|
if (!browser) {
|
2019-05-24 22:23:43 +02:00
|
|
|
return;
|
2023-01-02 23:59:03 +01:00
|
|
|
}
|
2019-05-24 22:23:43 +02:00
|
|
|
|
2021-05-14 18:58:55 +02:00
|
|
|
std::vector<CefDraggableRegion> draggable_regions;
|
|
|
|
if (regions) {
|
|
|
|
draggable_regions.reserve(regions->size());
|
|
|
|
|
|
|
|
for (const auto& region : *regions) {
|
|
|
|
const auto& rect = region->bounds;
|
|
|
|
const CefRect bounds(rect.x(), rect.y(), rect.width(), rect.height());
|
|
|
|
draggable_regions.push_back(
|
|
|
|
CefDraggableRegion(bounds, region->draggable));
|
|
|
|
}
|
2013-05-31 17:34:53 +02:00
|
|
|
}
|
|
|
|
|
2021-09-15 13:40:08 +02:00
|
|
|
// Delegate to BrowserInfo so that current state is maintained with
|
|
|
|
// cross-origin navigation.
|
|
|
|
browser_info_->MaybeNotifyDraggableRegionsChanged(
|
|
|
|
browser, this, std::move(draggable_regions));
|
2013-05-31 17:34:53 +02:00
|
|
|
}
|
2017-05-31 17:33:30 +02:00
|
|
|
|
2022-08-16 19:10:43 +02:00
|
|
|
std::string CefFrameHostImpl::GetDebugString() const {
|
|
|
|
return "frame " + frame_util::GetFrameDebugString(frame_id_) +
|
|
|
|
(is_main_frame_ ? " (main)" : " (sub)");
|
|
|
|
}
|
|
|
|
|
2017-05-31 17:33:30 +02:00
|
|
|
void CefExecuteJavaScriptWithUserGestureForTests(CefRefPtr<CefFrame> frame,
|
|
|
|
const CefString& javascript) {
|
|
|
|
CefFrameHostImpl* impl = static_cast<CefFrameHostImpl*>(frame.get());
|
2023-01-02 23:59:03 +01:00
|
|
|
if (impl) {
|
2019-05-11 00:14:48 +02:00
|
|
|
impl->ExecuteJavaScriptWithUserGestureForTests(javascript);
|
2023-01-02 23:59:03 +01:00
|
|
|
}
|
2017-05-31 17:33:30 +02:00
|
|
|
}
|