mirror of
https://bitbucket.org/chromiumembedded/cef
synced 2025-06-05 21:39:12 +02:00
Add CefPermissionHandler callbacks for permission prompts (see issue #3352)
This commit is contained in:
493
tests/ceftests/permission_prompt_unittest.cc
Normal file
493
tests/ceftests/permission_prompt_unittest.cc
Normal file
@@ -0,0 +1,493 @@
|
||||
// Copyright 2022 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.
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "include/base/cef_bind.h"
|
||||
#include "include/cef_parser.h"
|
||||
#include "include/cef_permission_handler.h"
|
||||
#include "include/cef_request_context_handler.h"
|
||||
#include "include/wrapper/cef_closure_task.h"
|
||||
#include "include/wrapper/cef_stream_resource_handler.h"
|
||||
#include "tests/ceftests/test_handler.h"
|
||||
#include "tests/ceftests/test_suite.h"
|
||||
#include "tests/gtest/include/gtest/gtest.h"
|
||||
#include "tests/shared/browser/client_app_browser.h"
|
||||
|
||||
namespace {
|
||||
|
||||
// Most permissions require HTTPS.
|
||||
constexpr char kPromptUrl[] = "https://permission-prompt-test/prompt.html";
|
||||
constexpr char kPromptOrigin[] = "https://permission-prompt-test/";
|
||||
|
||||
constexpr char kPromptNavUrl[] = "https://permission-prompt-test/nav.html";
|
||||
|
||||
class TestSetup {
|
||||
public:
|
||||
TestSetup() {}
|
||||
|
||||
// CONFIGURATION
|
||||
|
||||
// Deny the prompt by returning false in OnShowPermissionPrompt.
|
||||
bool deny_implicitly = false;
|
||||
|
||||
// Deny the prompt (implicitly) by not triggering it via a user gesture to
|
||||
// begin with.
|
||||
bool deny_no_gesture = false;
|
||||
|
||||
// Deny the prompt by returning true in OnShowPermissionPrompt but then never
|
||||
// calling CefPermissionPromptCallback::Continue.
|
||||
bool deny_with_navigation = false;
|
||||
|
||||
// Don't synchronously execute the callback in OnShowPermissionPrompt.
|
||||
bool continue_async = false;
|
||||
|
||||
// RESULTS
|
||||
|
||||
// Method callbacks.
|
||||
TrackCallback got_prompt;
|
||||
TrackCallback got_dismiss;
|
||||
|
||||
// JS success state.
|
||||
TrackCallback got_js_success;
|
||||
TrackCallback got_js_success_data;
|
||||
|
||||
// JS error state.
|
||||
TrackCallback got_js_error;
|
||||
std::string js_error_str;
|
||||
|
||||
// JS timeout state.
|
||||
TrackCallback got_js_timeout;
|
||||
};
|
||||
|
||||
class PermissionPromptTestHandler : public TestHandler,
|
||||
public CefPermissionHandler {
|
||||
public:
|
||||
PermissionPromptTestHandler(TestSetup* tr,
|
||||
uint32 request,
|
||||
cef_permission_request_result_t result)
|
||||
: test_setup_(tr), request_(request), result_(result) {}
|
||||
|
||||
cef_return_value_t OnBeforeResourceLoad(
|
||||
CefRefPtr<CefBrowser> browser,
|
||||
CefRefPtr<CefFrame> frame,
|
||||
CefRefPtr<CefRequest> request,
|
||||
CefRefPtr<CefCallback> callback) override {
|
||||
std::string newUrl = request->GetURL();
|
||||
if (newUrl.find("tests/exit") != std::string::npos) {
|
||||
if (newUrl.find("SUCCESS") != std::string::npos) {
|
||||
EXPECT_FALSE(test_setup_->got_js_success);
|
||||
test_setup_->got_js_success.yes();
|
||||
|
||||
auto dict = ParseURLData(newUrl);
|
||||
if (dict->GetBool("got_data")) {
|
||||
test_setup_->got_js_success_data.yes();
|
||||
}
|
||||
} else if (newUrl.find("ERROR") != std::string::npos) {
|
||||
EXPECT_FALSE(test_setup_->got_js_error);
|
||||
test_setup_->got_js_error.yes();
|
||||
|
||||
auto dict = ParseURLData(newUrl);
|
||||
test_setup_->js_error_str = dict->GetString("error_str");
|
||||
} else if (newUrl.find("TIMEOUT") != std::string::npos) {
|
||||
EXPECT_FALSE(test_setup_->got_js_timeout);
|
||||
test_setup_->got_js_timeout.yes();
|
||||
}
|
||||
|
||||
DestroyTest();
|
||||
return RV_CANCEL;
|
||||
}
|
||||
|
||||
return RV_CONTINUE;
|
||||
}
|
||||
|
||||
void RunTest() override {
|
||||
std::string page =
|
||||
"<html><head>"
|
||||
"<script>"
|
||||
"function onResult(val, data) {"
|
||||
" if(!data) {"
|
||||
" data = {};"
|
||||
" }"
|
||||
" document.location = "
|
||||
"`http://tests/"
|
||||
"exit?result=${val}&data=${encodeURIComponent(JSON.stringify(data))}`;"
|
||||
"}"
|
||||
"function makeRequest() {";
|
||||
|
||||
if (request_ == CEF_PERMISSION_TYPE_WINDOW_PLACEMENT) {
|
||||
page +=
|
||||
" window.getScreenDetails().then(function(details) {"
|
||||
" onResult(`SUCCESS`, {got_data: details.screens.length > 0});"
|
||||
" })";
|
||||
}
|
||||
|
||||
page +=
|
||||
" .catch(function(err) {"
|
||||
" console.log(err.toString());"
|
||||
" onResult(`ERROR`, {error_str: err.toString()});"
|
||||
" });";
|
||||
|
||||
if (test_setup_->deny_implicitly) {
|
||||
// Implicit IGNORE result means the promise will never resolve, so add a
|
||||
// timeout.
|
||||
page += " setTimeout(() => { onResult(`TIMEOUT`); }, 1000);";
|
||||
} else if (test_setup_->deny_with_navigation) {
|
||||
// Cancel the pending permission request by navigating.
|
||||
page += " setTimeout(() => { document.location = '" +
|
||||
std::string(kPromptNavUrl) + "'; }, 1000);";
|
||||
}
|
||||
|
||||
page +=
|
||||
"}"
|
||||
"</script>"
|
||||
"</head><body>";
|
||||
|
||||
if (test_setup_->deny_no_gesture) {
|
||||
// Expect this request to be blocked. See comments on OnLoadEnd.
|
||||
page += "<script>makeRequest();</script>";
|
||||
} else {
|
||||
page += "<a href='#' onclick='makeRequest(); return false;'>CLICK ME</a>";
|
||||
}
|
||||
|
||||
page += "</body></html>";
|
||||
|
||||
// Create the request context that will use an in-memory cache.
|
||||
CefRequestContextSettings settings;
|
||||
CefRefPtr<CefRequestContext> request_context =
|
||||
CefRequestContext::CreateContext(settings, nullptr);
|
||||
|
||||
AddResource(kPromptUrl, page, "text/html");
|
||||
|
||||
if (test_setup_->deny_with_navigation) {
|
||||
AddResource(kPromptNavUrl, "<html><body>Navigated</body></html>",
|
||||
"text/html");
|
||||
}
|
||||
|
||||
// Create the browser.
|
||||
CreateBrowser(kPromptUrl, request_context);
|
||||
|
||||
// Time out the test after a reasonable period of time.
|
||||
SetTestTimeout();
|
||||
}
|
||||
|
||||
CefRefPtr<CefPermissionHandler> GetPermissionHandler() override {
|
||||
return this;
|
||||
}
|
||||
|
||||
void OnLoadEnd(CefRefPtr<CefBrowser> browser,
|
||||
CefRefPtr<CefFrame> frame,
|
||||
int httpStatusCode) override {
|
||||
if (test_setup_->deny_no_gesture)
|
||||
return;
|
||||
|
||||
if (test_setup_->deny_with_navigation) {
|
||||
if (frame->GetURL().ToString() == kPromptNavUrl) {
|
||||
DestroyTest();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Begin the permissions request by clicking a link. This is necessary
|
||||
// because some prompts may be blocked without a transient user activation
|
||||
// (HasTransientUserActivation returning true in Chromium).
|
||||
SendClick(browser);
|
||||
}
|
||||
|
||||
bool OnShowPermissionPrompt(
|
||||
CefRefPtr<CefBrowser> browser,
|
||||
uint64 prompt_id,
|
||||
const CefString& requesting_origin,
|
||||
uint32 requested_permissions,
|
||||
CefRefPtr<CefPermissionPromptCallback> callback) override {
|
||||
EXPECT_UI_THREAD();
|
||||
|
||||
prompt_id_ = prompt_id;
|
||||
EXPECT_GT(prompt_id, 0U);
|
||||
|
||||
EXPECT_EQ(request_, requested_permissions);
|
||||
EXPECT_STREQ(kPromptOrigin, requesting_origin.ToString().c_str());
|
||||
|
||||
EXPECT_FALSE(test_setup_->got_prompt);
|
||||
test_setup_->got_prompt.yes();
|
||||
|
||||
if (test_setup_->deny_implicitly) {
|
||||
// Causes implicit IGNORE result for the permission request.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (test_setup_->deny_with_navigation) {
|
||||
// Handle the permission request, but never execute the callback.
|
||||
return true;
|
||||
}
|
||||
|
||||
if (test_setup_->continue_async) {
|
||||
CefPostTask(TID_UI, base::BindOnce(&CefPermissionPromptCallback::Continue,
|
||||
callback, result_));
|
||||
} else {
|
||||
callback->Continue(result_);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void OnDismissPermissionPrompt(
|
||||
CefRefPtr<CefBrowser> browser,
|
||||
uint64 prompt_id,
|
||||
cef_permission_request_result_t result) override {
|
||||
EXPECT_UI_THREAD();
|
||||
EXPECT_EQ(prompt_id_, prompt_id);
|
||||
EXPECT_EQ(result_, result);
|
||||
EXPECT_FALSE(test_setup_->got_dismiss);
|
||||
test_setup_->got_dismiss.yes();
|
||||
}
|
||||
|
||||
void DestroyTest() override {
|
||||
const size_t js_outcome_ct = test_setup_->got_js_success +
|
||||
test_setup_->got_js_error +
|
||||
test_setup_->got_js_timeout;
|
||||
if (test_setup_->deny_with_navigation) {
|
||||
// Expect no JS outcome.
|
||||
EXPECT_EQ(0U, js_outcome_ct);
|
||||
} else {
|
||||
// Expect a single JS outcome.
|
||||
EXPECT_EQ(1U, js_outcome_ct);
|
||||
}
|
||||
|
||||
TestHandler::DestroyTest();
|
||||
}
|
||||
|
||||
private:
|
||||
void SendClick(CefRefPtr<CefBrowser> browser) {
|
||||
CefMouseEvent mouse_event;
|
||||
mouse_event.x = 20;
|
||||
mouse_event.y = 20;
|
||||
|
||||
// Add some delay to avoid having events dropped or rate limited.
|
||||
CefPostDelayedTask(
|
||||
TID_UI,
|
||||
base::BindOnce(&CefBrowserHost::SendMouseClickEvent, browser->GetHost(),
|
||||
mouse_event, MBT_LEFT, false, 1),
|
||||
50);
|
||||
CefPostDelayedTask(
|
||||
TID_UI,
|
||||
base::BindOnce(&CefBrowserHost::SendMouseClickEvent, browser->GetHost(),
|
||||
mouse_event, MBT_LEFT, true, 1),
|
||||
100);
|
||||
}
|
||||
|
||||
CefRefPtr<CefDictionaryValue> ParseURLData(const std::string& url) {
|
||||
const std::string& find_str = "&data=";
|
||||
const std::string& data_string =
|
||||
url.substr(url.find(find_str) + std::string(find_str).length());
|
||||
const std::string& data_string_decoded = CefURIDecode(
|
||||
data_string, false,
|
||||
static_cast<cef_uri_unescape_rule_t>(
|
||||
UU_SPACES | UU_URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS));
|
||||
auto obj =
|
||||
CefParseJSON(data_string_decoded, JSON_PARSER_ALLOW_TRAILING_COMMAS);
|
||||
return obj->GetDictionary();
|
||||
}
|
||||
|
||||
TestSetup* const test_setup_;
|
||||
const uint32 request_;
|
||||
const cef_permission_request_result_t result_;
|
||||
uint64 prompt_id_ = 0U;
|
||||
|
||||
IMPLEMENT_REFCOUNTING(PermissionPromptTestHandler);
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
// Window placement permission requests.
|
||||
TEST(PermissionPromptTest, WindowPlacementReturningFalse) {
|
||||
TestSetup test_setup;
|
||||
test_setup.deny_implicitly = true;
|
||||
|
||||
CefRefPtr<PermissionPromptTestHandler> handler =
|
||||
new PermissionPromptTestHandler(&test_setup,
|
||||
CEF_PERMISSION_TYPE_WINDOW_PLACEMENT,
|
||||
CEF_PERMISSION_RESULT_IGNORE);
|
||||
handler->ExecuteTest();
|
||||
ReleaseAndWaitForDestructor(handler);
|
||||
|
||||
// No OnDismissPermissionPrompt callback for default handling.
|
||||
EXPECT_TRUE(test_setup.got_prompt);
|
||||
EXPECT_TRUE(test_setup.got_js_timeout);
|
||||
EXPECT_FALSE(test_setup.got_dismiss);
|
||||
}
|
||||
|
||||
TEST(PermissionPromptTest, WindowPlacementNoGesture) {
|
||||
TestSetup test_setup;
|
||||
test_setup.deny_no_gesture = true;
|
||||
|
||||
CefRefPtr<PermissionPromptTestHandler> handler =
|
||||
new PermissionPromptTestHandler(&test_setup,
|
||||
CEF_PERMISSION_TYPE_WINDOW_PLACEMENT,
|
||||
CEF_PERMISSION_RESULT_IGNORE);
|
||||
handler->ExecuteTest();
|
||||
ReleaseAndWaitForDestructor(handler);
|
||||
|
||||
// No OnShowPermissionPrompt or OnDismissPermissionPrompt callbacks for
|
||||
// prompts that are blocked.
|
||||
EXPECT_FALSE(test_setup.got_prompt);
|
||||
EXPECT_TRUE(test_setup.got_js_error);
|
||||
EXPECT_STREQ("NotAllowedError: Permission decision deferred.",
|
||||
test_setup.js_error_str.c_str());
|
||||
EXPECT_FALSE(test_setup.got_dismiss);
|
||||
}
|
||||
|
||||
TEST(PermissionPromptTest, WindowPlacementNoContinue) {
|
||||
TestSetup test_setup;
|
||||
test_setup.deny_with_navigation = true;
|
||||
|
||||
CefRefPtr<PermissionPromptTestHandler> handler =
|
||||
new PermissionPromptTestHandler(&test_setup,
|
||||
CEF_PERMISSION_TYPE_WINDOW_PLACEMENT,
|
||||
CEF_PERMISSION_RESULT_IGNORE);
|
||||
handler->ExecuteTest();
|
||||
ReleaseAndWaitForDestructor(handler);
|
||||
|
||||
// Callbacks but no JS result.
|
||||
EXPECT_TRUE(test_setup.got_prompt);
|
||||
EXPECT_TRUE(test_setup.got_dismiss);
|
||||
}
|
||||
|
||||
TEST(PermissionPromptTest, WindowPlacementResultAccept) {
|
||||
TestSetup test_setup;
|
||||
|
||||
CefRefPtr<PermissionPromptTestHandler> handler =
|
||||
new PermissionPromptTestHandler(&test_setup,
|
||||
CEF_PERMISSION_TYPE_WINDOW_PLACEMENT,
|
||||
CEF_PERMISSION_RESULT_ACCEPT);
|
||||
handler->ExecuteTest();
|
||||
ReleaseAndWaitForDestructor(handler);
|
||||
|
||||
EXPECT_TRUE(test_setup.got_prompt);
|
||||
EXPECT_TRUE(test_setup.got_js_success);
|
||||
EXPECT_TRUE(test_setup.got_js_success_data);
|
||||
EXPECT_TRUE(test_setup.got_dismiss);
|
||||
}
|
||||
|
||||
TEST(PermissionPromptTest, WindowPlacementResultAcceptAsync) {
|
||||
TestSetup test_setup;
|
||||
test_setup.continue_async = true;
|
||||
|
||||
CefRefPtr<PermissionPromptTestHandler> handler =
|
||||
new PermissionPromptTestHandler(&test_setup,
|
||||
CEF_PERMISSION_TYPE_WINDOW_PLACEMENT,
|
||||
CEF_PERMISSION_RESULT_ACCEPT);
|
||||
handler->ExecuteTest();
|
||||
ReleaseAndWaitForDestructor(handler);
|
||||
|
||||
EXPECT_TRUE(test_setup.got_prompt);
|
||||
EXPECT_TRUE(test_setup.got_js_success);
|
||||
EXPECT_TRUE(test_setup.got_js_success_data);
|
||||
EXPECT_TRUE(test_setup.got_dismiss);
|
||||
}
|
||||
|
||||
TEST(PermissionPromptTest, WindowPlacementResultDeny) {
|
||||
TestSetup test_setup;
|
||||
|
||||
CefRefPtr<PermissionPromptTestHandler> handler =
|
||||
new PermissionPromptTestHandler(&test_setup,
|
||||
CEF_PERMISSION_TYPE_WINDOW_PLACEMENT,
|
||||
CEF_PERMISSION_RESULT_DENY);
|
||||
handler->ExecuteTest();
|
||||
ReleaseAndWaitForDestructor(handler);
|
||||
|
||||
EXPECT_TRUE(test_setup.got_prompt);
|
||||
EXPECT_TRUE(test_setup.got_js_error);
|
||||
EXPECT_STREQ("NotAllowedError: Permission denied.",
|
||||
test_setup.js_error_str.c_str());
|
||||
EXPECT_TRUE(test_setup.got_dismiss);
|
||||
}
|
||||
|
||||
TEST(PermissionPromptTest, WindowPlacementResultDenyAsync) {
|
||||
TestSetup test_setup;
|
||||
test_setup.continue_async = true;
|
||||
|
||||
CefRefPtr<PermissionPromptTestHandler> handler =
|
||||
new PermissionPromptTestHandler(&test_setup,
|
||||
CEF_PERMISSION_TYPE_WINDOW_PLACEMENT,
|
||||
CEF_PERMISSION_RESULT_DENY);
|
||||
handler->ExecuteTest();
|
||||
ReleaseAndWaitForDestructor(handler);
|
||||
|
||||
EXPECT_TRUE(test_setup.got_prompt);
|
||||
EXPECT_TRUE(test_setup.got_js_error);
|
||||
EXPECT_STREQ("NotAllowedError: Permission denied.",
|
||||
test_setup.js_error_str.c_str());
|
||||
EXPECT_TRUE(test_setup.got_dismiss);
|
||||
}
|
||||
|
||||
TEST(PermissionPromptTest, WindowPlacementResultDismiss) {
|
||||
TestSetup test_setup;
|
||||
|
||||
CefRefPtr<PermissionPromptTestHandler> handler =
|
||||
new PermissionPromptTestHandler(&test_setup,
|
||||
CEF_PERMISSION_TYPE_WINDOW_PLACEMENT,
|
||||
CEF_PERMISSION_RESULT_DISMISS);
|
||||
handler->ExecuteTest();
|
||||
ReleaseAndWaitForDestructor(handler);
|
||||
|
||||
EXPECT_TRUE(test_setup.got_prompt);
|
||||
EXPECT_TRUE(test_setup.got_js_error);
|
||||
EXPECT_STREQ("NotAllowedError: Permission decision deferred.",
|
||||
test_setup.js_error_str.c_str());
|
||||
EXPECT_TRUE(test_setup.got_dismiss);
|
||||
}
|
||||
|
||||
TEST(PermissionPromptTest, WindowPlacementResultDismissAsync) {
|
||||
TestSetup test_setup;
|
||||
test_setup.continue_async = true;
|
||||
|
||||
CefRefPtr<PermissionPromptTestHandler> handler =
|
||||
new PermissionPromptTestHandler(&test_setup,
|
||||
CEF_PERMISSION_TYPE_WINDOW_PLACEMENT,
|
||||
CEF_PERMISSION_RESULT_DISMISS);
|
||||
handler->ExecuteTest();
|
||||
ReleaseAndWaitForDestructor(handler);
|
||||
|
||||
EXPECT_TRUE(test_setup.got_prompt);
|
||||
EXPECT_TRUE(test_setup.got_js_error);
|
||||
EXPECT_STREQ("NotAllowedError: Permission decision deferred.",
|
||||
test_setup.js_error_str.c_str());
|
||||
EXPECT_TRUE(test_setup.got_dismiss);
|
||||
}
|
||||
|
||||
TEST(PermissionPromptTest, WindowPlacementResultIgnore) {
|
||||
TestSetup test_setup;
|
||||
|
||||
CefRefPtr<PermissionPromptTestHandler> handler =
|
||||
new PermissionPromptTestHandler(&test_setup,
|
||||
CEF_PERMISSION_TYPE_WINDOW_PLACEMENT,
|
||||
CEF_PERMISSION_RESULT_IGNORE);
|
||||
handler->ExecuteTest();
|
||||
ReleaseAndWaitForDestructor(handler);
|
||||
|
||||
EXPECT_TRUE(test_setup.got_prompt);
|
||||
EXPECT_TRUE(test_setup.got_js_error);
|
||||
EXPECT_STREQ("NotAllowedError: Permission decision deferred.",
|
||||
test_setup.js_error_str.c_str());
|
||||
EXPECT_TRUE(test_setup.got_dismiss);
|
||||
}
|
||||
|
||||
TEST(PermissionPromptTest, WindowPlacementResultIgnoreAsync) {
|
||||
TestSetup test_setup;
|
||||
test_setup.continue_async = true;
|
||||
|
||||
CefRefPtr<PermissionPromptTestHandler> handler =
|
||||
new PermissionPromptTestHandler(&test_setup,
|
||||
CEF_PERMISSION_TYPE_WINDOW_PLACEMENT,
|
||||
CEF_PERMISSION_RESULT_IGNORE);
|
||||
handler->ExecuteTest();
|
||||
ReleaseAndWaitForDestructor(handler);
|
||||
|
||||
EXPECT_TRUE(test_setup.got_prompt);
|
||||
EXPECT_TRUE(test_setup.got_js_error);
|
||||
EXPECT_STREQ("NotAllowedError: Permission decision deferred.",
|
||||
test_setup.js_error_str.c_str());
|
||||
EXPECT_TRUE(test_setup.got_dismiss);
|
||||
}
|
Reference in New Issue
Block a user