blob: 31fc6eed4d1ac223e0f874a65d5a583cb4d316cf [file] [log] [blame]
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/keyboard_lock/keyboard_lock_service_impl.h"
#include <memory>
#include <optional>
#include <utility>
#include "base/check.h"
#include "base/containers/flat_set.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/metrics/histogram_macros.h"
#include "content/browser/keyboard_lock/keyboard_lock_metrics.h"
#include "content/browser/renderer_host/render_frame_host_impl.h"
#include "content/browser/renderer_host/render_widget_host_impl.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_widget_host.h"
#include "content/public/common/content_features.h"
#include "services/network/public/mojom/permissions_policy/permissions_policy_feature.mojom.h"
#include "third_party/blink/public/mojom/devtools/console_message.mojom.h"
#include "ui/events/keycodes/dom/dom_code.h"
#include "ui/events/keycodes/dom/keycode_converter.h"
using blink::mojom::GetKeyboardLayoutMapResult;
using blink::mojom::KeyboardLockRequestResult;
namespace content {
namespace {
void LogKeyboardLockMethodCalled(KeyboardLockMethods method) {
UMA_HISTOGRAM_ENUMERATION(kKeyboardLockMethodCalledHistogramName, method,
KeyboardLockMethods::kCount);
}
} // namespace
KeyboardLockServiceImpl::KeyboardLockServiceImpl(
RenderFrameHost& render_frame_host,
mojo::PendingReceiver<blink::mojom::KeyboardLockService> receiver)
: DocumentService(render_frame_host, std::move(receiver)) {}
// static
void KeyboardLockServiceImpl::CreateMojoService(
RenderFrameHost* render_frame_host,
mojo::PendingReceiver<blink::mojom::KeyboardLockService> receiver) {
CHECK(render_frame_host);
// The object is bound to the lifetime of |render_frame_host| and the mojo
// connection. See DocumentService for details.
new KeyboardLockServiceImpl(*render_frame_host, std::move(receiver));
}
void KeyboardLockServiceImpl::RequestKeyboardLock(
const std::vector<std::string>& key_codes,
RequestKeyboardLockCallback callback) {
if (key_codes.empty()) {
LogKeyboardLockMethodCalled(KeyboardLockMethods::kRequestAllKeys);
} else {
LogKeyboardLockMethodCalled(KeyboardLockMethods::kRequestSomeKeys);
}
if (render_frame_host().GetParentOrOuterDocument()) {
std::move(callback).Run(KeyboardLockRequestResult::kChildFrameError);
return;
}
if (!render_frame_host().IsActive()) {
std::move(callback).Run(KeyboardLockRequestResult::kFrameDetachedError);
return;
}
// Per base::flat_set usage notes, the proper way to init a flat_set is
// inserting into a vector and using that to init the flat_set.
std::vector<ui::DomCode> dom_codes;
bool invalid_key_code_found = false;
for (const std::string& code : key_codes) {
ui::DomCode dom_code = ui::KeycodeConverter::CodeStringToDomCode(code);
if (dom_code != ui::DomCode::NONE) {
dom_codes.push_back(dom_code);
} else {
invalid_key_code_found = true;
render_frame_host().AddMessageToConsole(
blink::mojom::ConsoleMessageLevel::kWarning,
"Invalid DOMString passed into keyboard.lock(): '" + code + "'");
}
}
auto& frame_host_impl =
static_cast<RenderFrameHostImpl&>(render_frame_host());
// If we are provided with a vector containing one or more invalid key codes,
// then exit without enabling keyboard lock. Also cancel any previous
// keyboard lock request since the most recent request failed.
if (invalid_key_code_found) {
frame_host_impl.GetRenderWidgetHost()->CancelKeyboardLock();
std::move(callback).Run(KeyboardLockRequestResult::kNoValidKeyCodesError);
return;
}
std::optional<base::flat_set<ui::DomCode>> dom_code_set;
if (!dom_codes.empty()) {
dom_code_set = std::move(dom_codes);
}
frame_host_impl.GetRenderWidgetHost()->RequestKeyboardLock(
std::move(dom_code_set),
// We use a lambda function here instead of binding to a method because
// we want the Mojo callback to be called even if `this` goes out of
// scope.
base::BindOnce(
[](base::WeakPtr<KeyboardLockServiceImpl> weak_this,
RequestKeyboardLockCallback callback,
blink::mojom::KeyboardLockRequestResult result) {
std::move(callback).Run(result);
if (weak_this &&
result == blink::mojom::KeyboardLockRequestResult::kSuccess) {
weak_this->feature_handle_ =
static_cast<RenderFrameHostImpl&>(
weak_this->render_frame_host())
.RegisterBackForwardCacheDisablingNonStickyFeature(
blink::scheduler::WebSchedulerTrackedFeature::
kKeyboardLock);
}
},
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void KeyboardLockServiceImpl::CancelKeyboardLock() {
LogKeyboardLockMethodCalled(KeyboardLockMethods::kCancelLock);
auto& frame_host_impl =
static_cast<RenderFrameHostImpl&>(render_frame_host());
frame_host_impl.GetRenderWidgetHost()->CancelKeyboardLock();
feature_handle_.Reset();
}
void KeyboardLockServiceImpl::GetKeyboardLayoutMap(
GetKeyboardLayoutMapCallback callback) {
auto& frame_host_impl =
static_cast<RenderFrameHostImpl&>(render_frame_host());
auto response = GetKeyboardLayoutMapResult::New();
// The keyboard layout map is only accessible from the outermost main frame or
// with the permission policy enabled.
if (frame_host_impl.GetParentOrOuterDocument() &&
!frame_host_impl.IsFeatureEnabled(
network::mojom::PermissionsPolicyFeature::kKeyboardMap)) {
response->status = blink::mojom::GetKeyboardLayoutMapStatus::kDenied;
std::move(callback).Run(std::move(response));
return;
}
response->status = blink::mojom::GetKeyboardLayoutMapStatus::kSuccess;
response->layout_map = frame_host_impl.GetPage().GetKeyboardLayoutMap();
std::move(callback).Run(std::move(response));
}
KeyboardLockServiceImpl::~KeyboardLockServiceImpl() = default;
} // namespace content