mirror of
https://bitbucket.org/chromiumembedded/cef
synced 2025-06-05 21:39:12 +02:00
Add visualization for Chrome configuration changes (fixes #3892)
- Add new API to retrieve/observe configuration values. - cefclient: Add https://tests/config to inspect configuration values in real time.
This commit is contained in:
334
tests/cefclient/browser/config_test.cc
Normal file
334
tests/cefclient/browser/config_test.cc
Normal file
@@ -0,0 +1,334 @@
|
||||
// Copyright (c) 2025 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 "tests/cefclient/browser/config_test.h"
|
||||
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "include/base/cef_logging.h"
|
||||
#include "include/cef_parser.h"
|
||||
#include "include/cef_request_context.h"
|
||||
#include "tests/cefclient/browser/test_runner.h"
|
||||
|
||||
namespace client::config_test {
|
||||
|
||||
namespace {
|
||||
|
||||
const char kTestUrlPath[] = "/config";
|
||||
|
||||
// Application-specific error codes.
|
||||
const int kMessageFormatError = 1;
|
||||
const int kRequestFailedError = 2;
|
||||
|
||||
// Common to all messages.
|
||||
const char kNameKey[] = "name";
|
||||
const char kNameGlobalConfig[] = "global_config";
|
||||
const char kNameSubscribe[] = "subscribe";
|
||||
|
||||
// Convert a dictionary value to a JSON string.
|
||||
CefString GetJSON(CefRefPtr<CefDictionaryValue> dictionary) {
|
||||
CefRefPtr<CefValue> value = CefValue::Create();
|
||||
value->SetDictionary(dictionary);
|
||||
return CefWriteJSON(value, JSON_WRITER_DEFAULT);
|
||||
}
|
||||
|
||||
using CallbackType = CefMessageRouterBrowserSide::Callback;
|
||||
|
||||
void SendSuccess(CefRefPtr<CallbackType> callback,
|
||||
CefRefPtr<CefDictionaryValue> result) {
|
||||
callback->Success(GetJSON(result));
|
||||
}
|
||||
|
||||
void SendFailure(CefRefPtr<CallbackType> callback,
|
||||
int error_code,
|
||||
const std::string& error_message) {
|
||||
callback->Failure(error_code, error_message);
|
||||
}
|
||||
|
||||
class PreferenceObserver : public CefPreferenceObserver {
|
||||
public:
|
||||
PreferenceObserver(CefRefPtr<CefPreferenceManager> manager,
|
||||
bool global,
|
||||
CefRefPtr<CallbackType> callback)
|
||||
: manager_(manager), global_(global), callback_(callback) {}
|
||||
|
||||
PreferenceObserver(const PreferenceObserver&) = delete;
|
||||
PreferenceObserver& operator=(const PreferenceObserver&) = delete;
|
||||
|
||||
void OnPreferenceChanged(const CefString& name) override {
|
||||
CEF_REQUIRE_UI_THREAD();
|
||||
auto value = manager_->GetPreference(name);
|
||||
|
||||
auto payload = CefDictionaryValue::Create();
|
||||
payload->SetString("type", "preference");
|
||||
payload->SetBool("global", global_);
|
||||
payload->SetString("name", name);
|
||||
if (value) {
|
||||
payload->SetInt("value_type", value->GetType());
|
||||
payload->SetValue("value", value);
|
||||
} else {
|
||||
payload->SetInt("value_type", VTYPE_NULL);
|
||||
payload->SetNull("value");
|
||||
}
|
||||
|
||||
SendSuccess(callback_, payload);
|
||||
}
|
||||
|
||||
private:
|
||||
const CefRefPtr<CefPreferenceManager> manager_;
|
||||
const bool global_;
|
||||
const CefRefPtr<CallbackType> callback_;
|
||||
|
||||
IMPLEMENT_REFCOUNTING(PreferenceObserver);
|
||||
};
|
||||
|
||||
class SettingObserver : public CefSettingObserver {
|
||||
public:
|
||||
SettingObserver(CefRefPtr<CefRequestContext> context,
|
||||
CefRefPtr<CallbackType> callback)
|
||||
: context_(context), callback_(callback) {}
|
||||
|
||||
SettingObserver(const SettingObserver&) = delete;
|
||||
SettingObserver& operator=(const SettingObserver&) = delete;
|
||||
|
||||
void OnSettingChanged(const CefString& requesting_url,
|
||||
const CefString& top_level_url,
|
||||
cef_content_setting_types_t content_type) override {
|
||||
CEF_REQUIRE_UI_THREAD();
|
||||
auto value = context_->GetWebsiteSetting(requesting_url, top_level_url,
|
||||
content_type);
|
||||
|
||||
auto payload = CefDictionaryValue::Create();
|
||||
payload->SetString("type", "setting");
|
||||
payload->SetString("requesting_url", requesting_url);
|
||||
payload->SetString("top_level_url", top_level_url);
|
||||
payload->SetInt("content_type", static_cast<int>(content_type));
|
||||
if (value) {
|
||||
payload->SetInt("value_type", value->GetType());
|
||||
payload->SetValue("value", value);
|
||||
} else {
|
||||
payload->SetInt("value_type", VTYPE_NULL);
|
||||
payload->SetNull("value");
|
||||
}
|
||||
|
||||
SendSuccess(callback_, payload);
|
||||
}
|
||||
|
||||
private:
|
||||
const CefRefPtr<CefRequestContext> context_;
|
||||
const CefRefPtr<CallbackType> callback_;
|
||||
|
||||
IMPLEMENT_REFCOUNTING(SettingObserver);
|
||||
};
|
||||
|
||||
// Handle messages in the browser process. Only accessed on the UI thread.
|
||||
class Handler : public CefMessageRouterBrowserSide::Handler {
|
||||
public:
|
||||
typedef std::vector<std::string> NameVector;
|
||||
|
||||
Handler() { CEF_REQUIRE_UI_THREAD(); }
|
||||
|
||||
Handler(const Handler&) = delete;
|
||||
Handler& operator=(const Handler&) = delete;
|
||||
|
||||
~Handler() override {
|
||||
for (auto& pair : subscription_state_map_) {
|
||||
delete pair.second;
|
||||
}
|
||||
}
|
||||
|
||||
// Called due to cefQuery execution in config.html.
|
||||
bool OnQuery(CefRefPtr<CefBrowser> browser,
|
||||
CefRefPtr<CefFrame> frame,
|
||||
int64_t query_id,
|
||||
const CefString& request,
|
||||
bool persistent,
|
||||
CefRefPtr<Callback> callback) override {
|
||||
CEF_REQUIRE_UI_THREAD();
|
||||
|
||||
// Only handle messages from the test URL.
|
||||
const std::string& url = frame->GetURL();
|
||||
if (!test_runner::IsTestURL(url, kTestUrlPath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Parse |request| as a JSON dictionary.
|
||||
CefRefPtr<CefDictionaryValue> request_dict = ParseJSON(request);
|
||||
if (!request_dict) {
|
||||
callback->Failure(kMessageFormatError, "Incorrect message format");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Verify the "name" key.
|
||||
if (!VerifyKey(request_dict, kNameKey, VTYPE_STRING, callback)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::string& message_name = request_dict->GetString(kNameKey);
|
||||
if (message_name == kNameGlobalConfig) {
|
||||
// JavaScript is requesting a JSON representation of the global config.
|
||||
|
||||
SendGlobalConfig(callback);
|
||||
return true;
|
||||
} else if (message_name == kNameSubscribe) {
|
||||
// Subscribe to notifications from observers.
|
||||
|
||||
if (!persistent) {
|
||||
SendFailure(callback, kMessageFormatError,
|
||||
"Subscriptions must be persistent");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!CreateSubscription(browser, query_id, callback)) {
|
||||
SendFailure(callback, kRequestFailedError,
|
||||
"Browser is already subscribed");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void OnQueryCanceled(CefRefPtr<CefBrowser> browser,
|
||||
CefRefPtr<CefFrame> frame,
|
||||
int64_t query_id) override {
|
||||
CEF_REQUIRE_UI_THREAD();
|
||||
RemoveSubscription(browser->GetIdentifier(), query_id);
|
||||
}
|
||||
|
||||
private:
|
||||
// Convert a JSON string to a dictionary value.
|
||||
static CefRefPtr<CefDictionaryValue> ParseJSON(const CefString& string) {
|
||||
CefRefPtr<CefValue> value = CefParseJSON(string, JSON_PARSER_RFC);
|
||||
if (value.get() && value->GetType() == VTYPE_DICTIONARY) {
|
||||
return value->GetDictionary();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Verify that |key| exists in |dictionary| and has type |value_type|. Fails
|
||||
// |callback| and returns false on failure.
|
||||
static bool VerifyKey(CefRefPtr<CefDictionaryValue> dictionary,
|
||||
const char* key,
|
||||
cef_value_type_t value_type,
|
||||
CefRefPtr<Callback> callback) {
|
||||
if (!dictionary->HasKey(key) || dictionary->GetType(key) != value_type) {
|
||||
callback->Failure(
|
||||
kMessageFormatError,
|
||||
"Missing or incorrectly formatted message key: " + std::string(key));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static CefRefPtr<CefListValue> MakeListValue(std::vector<CefString> vec) {
|
||||
if (vec.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
auto list = CefListValue::Create();
|
||||
list->SetSize(vec.size());
|
||||
size_t idx = 0;
|
||||
for (const auto& val : vec) {
|
||||
list->SetString(idx++, val);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
void SendGlobalConfig(CefRefPtr<Callback> callback) {
|
||||
std::vector<CefString> switches;
|
||||
CefPreferenceManager::GetChromeVariationsAsSwitches(switches);
|
||||
std::vector<CefString> strings;
|
||||
CefPreferenceManager::GetChromeVariationsAsStrings(strings);
|
||||
|
||||
auto payload = CefDictionaryValue::Create();
|
||||
|
||||
if (auto list = MakeListValue(switches)) {
|
||||
payload->SetList("switches", list);
|
||||
} else {
|
||||
payload->SetNull("switches");
|
||||
}
|
||||
|
||||
if (auto list = MakeListValue(strings)) {
|
||||
payload->SetList("strings", list);
|
||||
} else {
|
||||
payload->SetNull("strings");
|
||||
}
|
||||
|
||||
SendSuccess(callback, payload);
|
||||
}
|
||||
|
||||
// Subscription state associated with a single browser.
|
||||
struct SubscriptionState {
|
||||
int64_t query_id;
|
||||
CefRefPtr<PreferenceObserver> global_pref_observer;
|
||||
CefRefPtr<CefRegistration> global_pref_registration;
|
||||
CefRefPtr<PreferenceObserver> context_pref_observer;
|
||||
CefRefPtr<CefRegistration> context_pref_registration;
|
||||
CefRefPtr<SettingObserver> context_setting_observer;
|
||||
CefRefPtr<CefRegistration> context_setting_registration;
|
||||
};
|
||||
|
||||
bool CreateSubscription(CefRefPtr<CefBrowser> browser,
|
||||
int64_t query_id,
|
||||
CefRefPtr<Callback> callback) {
|
||||
const int browser_id = browser->GetIdentifier();
|
||||
if (subscription_state_map_.find(browser_id) !=
|
||||
subscription_state_map_.end()) {
|
||||
// An subscription already exists for this browser.
|
||||
return false;
|
||||
}
|
||||
|
||||
auto global_pref_manager =
|
||||
CefPreferenceManager::GetGlobalPreferenceManager();
|
||||
auto request_context = browser->GetHost()->GetRequestContext();
|
||||
|
||||
SubscriptionState* state = new SubscriptionState();
|
||||
state->query_id = query_id;
|
||||
|
||||
state->global_pref_observer =
|
||||
new PreferenceObserver(global_pref_manager, /*global=*/true, callback);
|
||||
state->global_pref_registration =
|
||||
global_pref_manager->AddPreferenceObserver(CefString(),
|
||||
state->global_pref_observer);
|
||||
|
||||
state->context_pref_observer =
|
||||
new PreferenceObserver(request_context, /*global=*/false, callback);
|
||||
state->context_pref_registration = request_context->AddPreferenceObserver(
|
||||
CefString(), state->context_pref_observer);
|
||||
|
||||
state->context_setting_observer =
|
||||
new SettingObserver(request_context, callback);
|
||||
state->context_setting_registration =
|
||||
request_context->AddSettingObserver(state->context_setting_observer);
|
||||
|
||||
subscription_state_map_[browser_id] = state;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void RemoveSubscription(int browser_id, int64_t query_id) {
|
||||
SubscriptionStateMap::iterator it =
|
||||
subscription_state_map_.find(browser_id);
|
||||
if (it != subscription_state_map_.end() &&
|
||||
it->second->query_id == query_id) {
|
||||
delete it->second;
|
||||
subscription_state_map_.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
// Map of browser ID to SubscriptionState object.
|
||||
typedef std::map<int, SubscriptionState*> SubscriptionStateMap;
|
||||
SubscriptionStateMap subscription_state_map_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
void CreateMessageHandlers(test_runner::MessageHandlerSet& handlers) {
|
||||
handlers.insert(new Handler());
|
||||
}
|
||||
|
||||
} // namespace client::config_test
|
18
tests/cefclient/browser/config_test.h
Normal file
18
tests/cefclient/browser/config_test.h
Normal file
@@ -0,0 +1,18 @@
|
||||
// Copyright (c) 2025 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.
|
||||
|
||||
#ifndef CEF_TESTS_CEFCLIENT_BROWSER_CONFIG_TEST_H_
|
||||
#define CEF_TESTS_CEFCLIENT_BROWSER_CONFIG_TEST_H_
|
||||
#pragma once
|
||||
|
||||
#include "tests/cefclient/browser/test_runner.h"
|
||||
|
||||
namespace client::config_test {
|
||||
|
||||
// Create message handlers. Called from test_runner.cc.
|
||||
void CreateMessageHandlers(test_runner::MessageHandlerSet& handlers);
|
||||
|
||||
} // namespace client::config_test
|
||||
|
||||
#endif // CEF_TESTS_CEFCLIENT_BROWSER_CONFIG_TEST_H_
|
@@ -46,32 +46,33 @@
|
||||
#define IDC_STATIC -1
|
||||
#define IDS_BINARY_TRANSFER_HTML 1000
|
||||
#define IDS_BINDING_HTML 1001
|
||||
#define IDS_DIALOGS_HTML 1002
|
||||
#define IDS_DRAGGABLE_HTML 1003
|
||||
#define IDS_HANG_HTML 1004
|
||||
#define IDS_IPC_PERFORMANCE_HTML 1005
|
||||
#define IDS_LOCALSTORAGE_HTML 1006
|
||||
#define IDS_LOGO_PNG 1007
|
||||
#define IDS_MEDIA_ROUTER_HTML 1008
|
||||
#define IDS_MENU_ICON_1X_PNG 1009
|
||||
#define IDS_MENU_ICON_2X_PNG 1010
|
||||
#define IDS_OSRTEST_HTML 1011
|
||||
#define IDS_OTHER_TESTS_HTML 1012
|
||||
#define IDS_PDF_HTML 1013
|
||||
#define IDS_PDF_PDF 1014
|
||||
#define IDS_PERFORMANCE_HTML 1015
|
||||
#define IDS_PERFORMANCE2_HTML 1016
|
||||
#define IDS_PREFERENCES_HTML 1017
|
||||
#define IDS_RESPONSE_FILTER_HTML 1018
|
||||
#define IDS_SERVER_HTML 1019
|
||||
#define IDS_TASK_MANAGER_HTML 1020
|
||||
#define IDS_TRANSPARENCY_HTML 1021
|
||||
#define IDS_URLREQUEST_HTML 1022
|
||||
#define IDS_WEBSOCKET_HTML 1023
|
||||
#define IDS_WINDOW_HTML 1024
|
||||
#define IDS_WINDOW_ICON_1X_PNG 1025
|
||||
#define IDS_WINDOW_ICON_2X_PNG 1026
|
||||
#define IDS_XMLHTTPREQUEST_HTML 1027
|
||||
#define IDS_CONFIG_HTML 1002
|
||||
#define IDS_DIALOGS_HTML 1003
|
||||
#define IDS_DRAGGABLE_HTML 1004
|
||||
#define IDS_HANG_HTML 1005
|
||||
#define IDS_IPC_PERFORMANCE_HTML 1006
|
||||
#define IDS_LOCALSTORAGE_HTML 1007
|
||||
#define IDS_LOGO_PNG 1008
|
||||
#define IDS_MEDIA_ROUTER_HTML 1009
|
||||
#define IDS_MENU_ICON_1X_PNG 1010
|
||||
#define IDS_MENU_ICON_2X_PNG 1011
|
||||
#define IDS_OSRTEST_HTML 1012
|
||||
#define IDS_OTHER_TESTS_HTML 1013
|
||||
#define IDS_PDF_HTML 1014
|
||||
#define IDS_PDF_PDF 1015
|
||||
#define IDS_PERFORMANCE_HTML 1016
|
||||
#define IDS_PERFORMANCE2_HTML 1017
|
||||
#define IDS_PREFERENCES_HTML 1018
|
||||
#define IDS_RESPONSE_FILTER_HTML 1019
|
||||
#define IDS_SERVER_HTML 1020
|
||||
#define IDS_TASK_MANAGER_HTML 1021
|
||||
#define IDS_TRANSPARENCY_HTML 1022
|
||||
#define IDS_URLREQUEST_HTML 1023
|
||||
#define IDS_WEBSOCKET_HTML 1024
|
||||
#define IDS_WINDOW_HTML 1025
|
||||
#define IDS_WINDOW_ICON_1X_PNG 1026
|
||||
#define IDS_WINDOW_ICON_2X_PNG 1027
|
||||
#define IDS_XMLHTTPREQUEST_HTML 1028
|
||||
|
||||
// Next default values for new objects
|
||||
//
|
||||
|
@@ -15,6 +15,7 @@ int GetResourceId(const char* resource_name) {
|
||||
int id;
|
||||
} resource_map[] = {{"binary_transfer.html", IDS_BINARY_TRANSFER_HTML},
|
||||
{"binding.html", IDS_BINDING_HTML},
|
||||
{"config.html", IDS_CONFIG_HTML},
|
||||
{"dialogs.html", IDS_DIALOGS_HTML},
|
||||
{"draggable.html", IDS_DRAGGABLE_HTML},
|
||||
{"hang.html", IDS_HANG_HTML},
|
||||
|
@@ -21,6 +21,7 @@
|
||||
#include "tests/cefclient/browser/base_client_handler.h"
|
||||
#include "tests/cefclient/browser/binary_transfer_test.h"
|
||||
#include "tests/cefclient/browser/binding_test.h"
|
||||
#include "tests/cefclient/browser/config_test.h"
|
||||
#include "tests/cefclient/browser/dialog_test.h"
|
||||
#include "tests/cefclient/browser/hang_test.h"
|
||||
#include "tests/cefclient/browser/main_context.h"
|
||||
@@ -865,6 +866,9 @@ void CreateMessageHandlers(MessageHandlerSet& handlers) {
|
||||
// Create the binding test handlers.
|
||||
binding_test::CreateMessageHandlers(handlers);
|
||||
|
||||
// Create the config test handlers.
|
||||
config_test::CreateMessageHandlers(handlers);
|
||||
|
||||
// Create the dialog test handlers.
|
||||
dialog_test::CreateMessageHandlers(handlers);
|
||||
|
||||
|
703
tests/cefclient/resources/config.html
Normal file
703
tests/cefclient/resources/config.html
Normal file
@@ -0,0 +1,703 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Configuration Test</title>
|
||||
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
|
||||
|
||||
<style>
|
||||
body {
|
||||
font-family: Verdana, Arial;
|
||||
font-size: 12px;
|
||||
}
|
||||
#message {
|
||||
color: red;
|
||||
font-weight: bold;
|
||||
font-size: 14px;
|
||||
}
|
||||
.desc {
|
||||
font-size: 14px;
|
||||
}
|
||||
.foot {
|
||||
font-size: 11px;
|
||||
}
|
||||
.mono {
|
||||
font-family: monospace;
|
||||
}
|
||||
.cat_header_0 {
|
||||
font-weight: bold;
|
||||
font-size: 14px;
|
||||
}
|
||||
.cat_header_1 {
|
||||
font-weight: bold;
|
||||
}
|
||||
.cat_header_2 {
|
||||
font-family: Verdana, Arial;
|
||||
}
|
||||
.cat_body {
|
||||
font-family: monospace;
|
||||
white-space: pre;
|
||||
margin-left: 10px;
|
||||
}
|
||||
#temp-message {
|
||||
display: none;
|
||||
background-color: #f0f0f0;
|
||||
border: 1px solid #ccc;
|
||||
padding: 10px;
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
.hr-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
}
|
||||
.hr-line {
|
||||
border-top: 1px solid black;
|
||||
width: 100%;
|
||||
margin: 0 3px;
|
||||
}
|
||||
.hr-text {
|
||||
padding: 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
function onLoad() {
|
||||
if (location.hostname != 'tests') {
|
||||
onCefError(0, 'This page can only be run from tests.');
|
||||
|
||||
// Disable all form elements.
|
||||
var elements = document.getElementById("form").elements;
|
||||
for (var i = 0, element; element = elements[i++]; ) {
|
||||
element.disabled = true;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
getGlobalConfig();
|
||||
updateFilter();
|
||||
startSubscription();
|
||||
}
|
||||
|
||||
function onUnload() {
|
||||
stopSubscription();
|
||||
}
|
||||
|
||||
function onCefError(code, message) {
|
||||
val = 'ERROR: ' + message;
|
||||
if (code !== 0) {
|
||||
val += ' (' + code + ')';
|
||||
}
|
||||
document.getElementById('message').innerHTML = val + '<br/><br/>';
|
||||
}
|
||||
|
||||
function sendCefQuery(payload, onSuccess, onFailure=onCefError, persistent=false) {
|
||||
// Results in a call to the OnQuery method in config_test.cc
|
||||
return window.cefQuery({
|
||||
request: JSON.stringify(payload),
|
||||
onSuccess: onSuccess,
|
||||
onFailure: onFailure,
|
||||
persistent: persistent
|
||||
});
|
||||
}
|
||||
|
||||
// Request the global configuration.
|
||||
function getGlobalConfig() {
|
||||
sendCefQuery(
|
||||
{name: 'global_config'},
|
||||
(message) => onGlobalConfigMessage(JSON.parse(message)),
|
||||
);
|
||||
}
|
||||
|
||||
// Display the global configuration response.
|
||||
function onGlobalConfigMessage(message) {
|
||||
document.getElementById('global_switches').innerHTML =
|
||||
message.switches !== null ? message.switches.join('<br/>') : '(none)';
|
||||
if (message.strings !== null) {
|
||||
document.getElementById('global_strings').innerHTML = message.strings.join('<br/>');
|
||||
document.getElementById('global_strings_ct').textContent = message.strings.length;
|
||||
}
|
||||
}
|
||||
|
||||
var currentSubscriptionId = null;
|
||||
|
||||
// Subscribe to ongoing message notifications from the native code.
|
||||
function startSubscription() {
|
||||
currentSubscriptionId = sendCefQuery(
|
||||
{name: 'subscribe'},
|
||||
(message) => onSubscriptionMessage(JSON.parse(message)),
|
||||
(code, message) => {
|
||||
onCefError(code, message);
|
||||
currentSubscriptionId = null;
|
||||
},
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
// Unsubscribe from message notifications.
|
||||
function stopSubscription() {
|
||||
if (currentSubscriptionId !== null) {
|
||||
// Results in a call to the OnQueryCanceled method in config_test.cc
|
||||
window.cefQueryCancel(currentSubscriptionId);
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a nice timestamp for display purposes.
|
||||
function getNiceTimestamp() {
|
||||
const now = new Date();
|
||||
|
||||
const year = now.getFullYear();
|
||||
const month = String(now.getMonth() + 1).padStart(2, '0'); // Months are 0-indexed
|
||||
const day = String(now.getDate()).padStart(2, '0');
|
||||
const hours = String(now.getHours()).padStart(2, '0');
|
||||
const minutes = String(now.getMinutes()).padStart(2, '0');
|
||||
const seconds = String(now.getSeconds()).padStart(2, '0');
|
||||
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
||||
}
|
||||
|
||||
var paused = false;
|
||||
var paused_messages = [];
|
||||
var first_after_pause = false;
|
||||
|
||||
// Toggle whether messages are displayed or queued.
|
||||
function togglePause() {
|
||||
paused = !paused;
|
||||
document.getElementById("pause_button").value = paused ? "Resume" : "Pause";
|
||||
|
||||
if (!paused) {
|
||||
first_after_pause = true;
|
||||
while (paused_messages.length > 0) {
|
||||
onSubscriptionMessage(paused_messages.shift());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function doPause() {
|
||||
if (!paused) {
|
||||
togglePause();
|
||||
showTempMessage('Event processing is paused. Click Resume to continue.');
|
||||
}
|
||||
}
|
||||
|
||||
var filter = {}
|
||||
var filtered_ct = 0;
|
||||
var filter_updating = false;
|
||||
|
||||
// Populate |filter| based on form control state.
|
||||
function updateFilter() {
|
||||
if (filter_updating) {
|
||||
// Ignore changes triggered from individual elements while we're updating multiple.
|
||||
return;
|
||||
}
|
||||
|
||||
filter.text = document.getElementById("filter_text").value.trim().toLowerCase();
|
||||
filter.global_prefs = document.getElementById("filter_global_prefs").checked;
|
||||
filter.context_prefs = document.getElementById("filter_context_prefs").checked;
|
||||
filter.context_settings = document.getElementById("filter_context_settings").checked;
|
||||
}
|
||||
|
||||
function doFilter(type, text, global=false) {
|
||||
filter_updating = true;
|
||||
|
||||
document.getElementById("filter_text").value = text;
|
||||
|
||||
var checked = '';
|
||||
if (type === 'preference') {
|
||||
checked = global ? 'filter_global_prefs' : 'filter_context_prefs';
|
||||
} else if (type === 'setting') {
|
||||
checked = 'filter_context_settings';
|
||||
}
|
||||
|
||||
['filter_global_prefs', 'filter_context_prefs', 'filter_context_settings'].forEach(function(id) {
|
||||
document.getElementById(id).checked = id === checked;
|
||||
});
|
||||
|
||||
filter_updating = false;
|
||||
updateFilter();
|
||||
}
|
||||
|
||||
function doFilterReset() {
|
||||
filter_updating = true;
|
||||
document.getElementById("filtered_ct").textContent = 0;
|
||||
document.getElementById("filter_text").value = '';
|
||||
document.getElementById("filter_global_prefs").checked = true;
|
||||
document.getElementById("filter_context_prefs").checked = true;
|
||||
document.getElementById("filter_context_settings").checked = true;
|
||||
filter_updating = false;
|
||||
updateFilter();
|
||||
}
|
||||
|
||||
// Returns true if the message should be displayed based on the current filter settings.
|
||||
function passesFilter(message) {
|
||||
if (message.type === 'preference') {
|
||||
if (message.global) {
|
||||
if (!filter.global_prefs) {
|
||||
return false;
|
||||
}
|
||||
} else if (!filter.context_prefs) {
|
||||
return false;
|
||||
}
|
||||
} else if (message.type === 'setting' && !filter.context_settings) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (filter.text.length > 0) {
|
||||
var check_text = JSON.stringify(message).toLowerCase();
|
||||
if (message.type === 'setting') {
|
||||
check_text += ' ' + getSettingTypeLabel(message.content_type).toLowerCase();
|
||||
}
|
||||
if (check_text.indexOf(filter.text) < 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Match the cef_value_type_t values from include/internal/cef_types.h
|
||||
const value_types = [
|
||||
'INVALID',
|
||||
'NULL',
|
||||
'BOOL',
|
||||
'INT',
|
||||
'DOUBLE',
|
||||
'STRING',
|
||||
'BINARY',
|
||||
'DICTIONARY',
|
||||
'LIST',
|
||||
]
|
||||
|
||||
function getValueType(index) {
|
||||
if (index < 0 || index >= value_types.length) {
|
||||
return 'UNKNOWN';
|
||||
}
|
||||
return value_types[index];
|
||||
}
|
||||
|
||||
// Match the cef_content_setting_types_t values from include/internal/cef_types_content_settings.h
|
||||
const setting_types = [
|
||||
"COOKIES",
|
||||
"IMAGES",
|
||||
"JAVASCRIPT",
|
||||
"POPUPS",
|
||||
"GEOLOCATION",
|
||||
"NOTIFICATIONS",
|
||||
"AUTO_SELECT_CERTIFICATE",
|
||||
"MIXEDSCRIPT",
|
||||
"MEDIASTREAM_MIC",
|
||||
"MEDIASTREAM_CAMERA",
|
||||
"PROTOCOL_HANDLERS",
|
||||
"DEPRECATED_PPAPI_BROKER",
|
||||
"AUTOMATIC_DOWNLOADS",
|
||||
"MIDI_SYSEX",
|
||||
"SSL_CERT_DECISIONS",
|
||||
"PROTECTED_MEDIA_IDENTIFIER",
|
||||
"APP_BANNER",
|
||||
"SITE_ENGAGEMENT",
|
||||
"DURABLE_STORAGE",
|
||||
"USB_CHOOSER_DATA",
|
||||
"BLUETOOTH_GUARD",
|
||||
"BACKGROUND_SYNC",
|
||||
"AUTOPLAY",
|
||||
"IMPORTANT_SITE_INFO",
|
||||
"PERMISSION_AUTOBLOCKER_DATA",
|
||||
"ADS",
|
||||
"ADS_DATA",
|
||||
"MIDI",
|
||||
"PASSWORD_PROTECTION",
|
||||
"MEDIA_ENGAGEMENT",
|
||||
"SOUND",
|
||||
"CLIENT_HINTS",
|
||||
"SENSORS",
|
||||
"DEPRECATED_ACCESSIBILITY_EVENTS",
|
||||
"PAYMENT_HANDLER",
|
||||
"USB_GUARD",
|
||||
"BACKGROUND_FETCH",
|
||||
"INTENT_PICKER_DISPLAY",
|
||||
"IDLE_DETECTION",
|
||||
"SERIAL_GUARD",
|
||||
"SERIAL_CHOOSER_DATA",
|
||||
"PERIODIC_BACKGROUND_SYNC",
|
||||
"BLUETOOTH_SCANNING",
|
||||
"HID_GUARD",
|
||||
"HID_CHOOSER_DATA",
|
||||
"WAKE_LOCK_SCREEN",
|
||||
"WAKE_LOCK_SYSTEM",
|
||||
"LEGACY_COOKIE_ACCESS",
|
||||
"FILE_SYSTEM_WRITE_GUARD",
|
||||
"NFC",
|
||||
"BLUETOOTH_CHOOSER_DATA",
|
||||
"CLIPBOARD_READ_WRITE",
|
||||
"CLIPBOARD_SANITIZED_WRITE",
|
||||
"SAFE_BROWSING_URL_CHECK_DATA",
|
||||
"VR",
|
||||
"AR",
|
||||
"FILE_SYSTEM_READ_GUARD",
|
||||
"STORAGE_ACCESS",
|
||||
"CAMERA_PAN_TILT_ZOOM",
|
||||
"WINDOW_MANAGEMENT",
|
||||
"INSECURE_PRIVATE_NETWORK",
|
||||
"LOCAL_FONTS",
|
||||
"PERMISSION_AUTOREVOCATION_DATA",
|
||||
"FILE_SYSTEM_LAST_PICKED_DIRECTORY",
|
||||
"DISPLAY_CAPTURE",
|
||||
"FILE_SYSTEM_ACCESS_CHOOSER_DATA",
|
||||
"FEDERATED_IDENTITY_SHARING",
|
||||
"JAVASCRIPT_JIT",
|
||||
"HTTP_ALLOWED",
|
||||
"FORMFILL_METADATA",
|
||||
"DEPRECATED_FEDERATED_IDENTITY_ACTIVE_SESSION",
|
||||
"AUTO_DARK_WEB_CONTENT",
|
||||
"REQUEST_DESKTOP_SITE",
|
||||
"FEDERATED_IDENTITY_API",
|
||||
"NOTIFICATION_INTERACTIONS",
|
||||
"REDUCED_ACCEPT_LANGUAGE",
|
||||
"NOTIFICATION_PERMISSION_REVIEW",
|
||||
"PRIVATE_NETWORK_GUARD",
|
||||
"PRIVATE_NETWORK_CHOOSER_DATA",
|
||||
"FEDERATED_IDENTITY_IDENTITY_PROVIDER_SIGNIN_STATUS",
|
||||
"REVOKED_UNUSED_SITE_PERMISSIONS",
|
||||
"TOP_LEVEL_STORAGE_ACCESS",
|
||||
"FEDERATED_IDENTITY_AUTO_REAUTHN_PERMISSION",
|
||||
"FEDERATED_IDENTITY_IDENTITY_PROVIDER_REGISTRATION",
|
||||
"ANTI_ABUSE",
|
||||
"THIRD_PARTY_STORAGE_PARTITIONING",
|
||||
"HTTPS_ENFORCED",
|
||||
"ALL_SCREEN_CAPTURE",
|
||||
"COOKIE_CONTROLS_METADATA",
|
||||
"TPCD_HEURISTICS_GRANTS",
|
||||
"TPCD_METADATA_GRANTS",
|
||||
"TPCD_TRIAL",
|
||||
"TOP_LEVEL_TPCD_TRIAL",
|
||||
"TOP_LEVEL_TPCD_ORIGIN_TRIAL",
|
||||
"AUTO_PICTURE_IN_PICTURE",
|
||||
"FILE_SYSTEM_ACCESS_EXTENDED_PERMISSION",
|
||||
"FILE_SYSTEM_ACCESS_RESTORE_PERMISSION",
|
||||
"CAPTURED_SURFACE_CONTROL",
|
||||
"SMART_CARD_GUARD",
|
||||
"SMART_CARD_DATA",
|
||||
"WEB_PRINTING",
|
||||
"AUTOMATIC_FULLSCREEN",
|
||||
"SUB_APP_INSTALLATION_PROMPTS",
|
||||
"SPEAKER_SELECTION",
|
||||
"DIRECT_SOCKETS",
|
||||
"KEYBOARD_LOCK",
|
||||
"POINTER_LOCK",
|
||||
"REVOKED_ABUSIVE_NOTIFICATION_PERMISSIONS",
|
||||
"TRACKING_PROTECTION",
|
||||
"DISPLAY_MEDIA_SYSTEM_AUDIO",
|
||||
"JAVASCRIPT_OPTIMIZER",
|
||||
"STORAGE_ACCESS_HEADER_ORIGIN_TRIAL",
|
||||
"HAND_TRACKING",
|
||||
"WEB_APP_INSTALLATION",
|
||||
"DIRECT_SOCKETS_PRIVATE_NETWORK_ACCESS",
|
||||
"LEGACY_COOKIE_SCOPE",
|
||||
"ARE_SUSPICIOUS_NOTIFICATIONS_ALLOWLISTED_BY_USER",
|
||||
"CONTROLLED_FRAME",
|
||||
];
|
||||
|
||||
function getSettingType(index) {
|
||||
if (index < 0 || index >= setting_types.length) {
|
||||
return 'UNKNOWN';
|
||||
}
|
||||
return setting_types[index];
|
||||
}
|
||||
|
||||
function getSettingTypeLabel(type) {
|
||||
return getSettingType(type) + ' (' + type + ')'
|
||||
}
|
||||
|
||||
function makeDetails(summaryHTML, summaryClass, contentHTML, contentClass, contentId=null, open=false) {
|
||||
const newDetails = document.createElement('details');
|
||||
if (open) {
|
||||
newDetails.open = true;
|
||||
}
|
||||
|
||||
const newSummary = document.createElement('summary');
|
||||
newSummary.innerHTML = summaryHTML;
|
||||
if (summaryClass !== null) {
|
||||
newSummary.className = summaryClass;
|
||||
}
|
||||
newDetails.append(newSummary);
|
||||
|
||||
const newContent = document.createElement('p');
|
||||
newContent.innerHTML = contentHTML
|
||||
if (contentClass !== null) {
|
||||
newContent.className = contentClass;
|
||||
}
|
||||
if (contentId !== null) {
|
||||
newContent.id = contentId;
|
||||
}
|
||||
newDetails.append(newContent);
|
||||
|
||||
const newP = document.createElement('p');
|
||||
newP.append(newDetails);
|
||||
|
||||
return newP;
|
||||
}
|
||||
|
||||
function makeValueExample(value, value_type) {
|
||||
code = '\n// Create a CefValue object programmatically:\n' +
|
||||
'auto value = CefValue::Create();\n';
|
||||
if (value === null || getValueType(value_type) == 'NULL') {
|
||||
code += 'value->SetNull();\n';
|
||||
} else if (typeof value === 'boolean' || getValueType(value_type) == 'BOOL') {
|
||||
code += 'value->SetBool(' + (value ? 'true' : 'false') + ');\n';
|
||||
} else if (Number.isInteger(value) || getValueType(value_type) == 'INT') {
|
||||
code += 'value->SetInt(' + value + ');\n';
|
||||
} else if (typeof value === 'number' || getValueType(value_type) == 'DOUBLE') {
|
||||
code += 'value->SetDouble(' + value + ');\n';
|
||||
} else if (typeof value === 'string' || getValueType(value_type) == 'STRING') {
|
||||
code += 'value->SetString("' + value + '");\n';
|
||||
} else if (Array.isArray(value) || getValueType(value_type) == 'LIST') {
|
||||
code += 'auto listValue = CefListValue::Create();\n';
|
||||
if (value.length > 0) {
|
||||
code += '\n// TODO: Populate |listValue| using CefListValue::Set* methods.\n\n';
|
||||
}
|
||||
code += 'value->SetList(listValue);\n';
|
||||
if (value.length > 0) {
|
||||
code += '\n// ALTERNATELY: Create a CefValue object by parsing a JSON string:\n' +
|
||||
'auto value = CefParseJSON("[ ... ]", JSON_PARSER_RFC);\n';
|
||||
}
|
||||
} else if (typeof value === 'object' || getValueType(value_type) == 'DICTIONARY') {
|
||||
code += 'auto dictValue = CefDictionaryValue::Create();\n';
|
||||
const has_value = Object.keys(value).length > 0;
|
||||
if (has_value) {
|
||||
code += '\n// TODO: Populate |dictValue| using CefDictionaryValue::Set* methods.\n\n';
|
||||
}
|
||||
code += 'value->SetDictionary(dictValue);\n';
|
||||
if (has_value) {
|
||||
code += '\n// ALTERNATELY: Create a CefValue object by parsing a JSON string:\n' +
|
||||
'auto value = CefParseJSON("{ ... }", JSON_PARSER_RFC);\n';
|
||||
}
|
||||
} else {
|
||||
code += '\n//TODO: Populate |value|.\n\n';
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
||||
function makeCopyLink(elem_id) {
|
||||
return '<a href="#" onMouseDown="copyToClipboard(\'' + elem_id + '\')" onClick="return false">[copy to clipboard]</a>';
|
||||
}
|
||||
|
||||
function makeContent(elem_id, content) {
|
||||
const content_id = 'cn-' + elem_id;
|
||||
return makeDetails('Content ' + makeCopyLink(content_id), 'cat_header_2', content, 'cat_body', content_id, true);
|
||||
}
|
||||
|
||||
function makeHR(label) {
|
||||
const container = document.createElement('div');
|
||||
container.className = 'hr-container';
|
||||
const line1 = document.createElement('div');
|
||||
line1.className = 'hr-line';
|
||||
container.append(line1);
|
||||
const text = document.createElement('span');
|
||||
text.className = 'hr-text';
|
||||
text.innerHTML = label;
|
||||
container.append(text);
|
||||
const line2 = document.createElement('div');
|
||||
line2.className = 'hr-line';
|
||||
container.append(line2);
|
||||
return container;
|
||||
}
|
||||
|
||||
function makeCodeExample(elem_id, message) {
|
||||
const example_id = 'ex-' + elem_id;
|
||||
var code = '// Code must be executed on the browser process UI thread.\n\n';
|
||||
|
||||
if (message.type === "preference") {
|
||||
if (message.global) {
|
||||
code += 'auto pref_manager = CefPreferenceManager::GetGlobalPreferenceManager();\n';
|
||||
} else {
|
||||
code += '// |browser| is an existing CefBrowser instance.\n' +
|
||||
'auto pref_manager = browser->GetHost()->GetRequestContext();\n';
|
||||
}
|
||||
code += makeValueExample(message.value, message.value_type) + '\n' +
|
||||
'CefString error;\n' +
|
||||
'bool success = pref_manager->SetPreference("' + message.name + '", value, error);\n' +
|
||||
'if (!success) {\n' +
|
||||
' // TODO: Use |error| to diagnose the failure.\n' +
|
||||
'}\n';
|
||||
} else if (message.type === "setting") {
|
||||
const type = getSettingType(message.content_type);
|
||||
const content_type = type !== 'UNKNOWN' ? 'CEF_CONTENT_SETTING_TYPE_' + type : message.content_type;
|
||||
|
||||
code += '// |browser| is an existing CefBrowser instance.\n' +
|
||||
'auto context = browser->GetHost()->GetRequestContext();\n' +
|
||||
makeValueExample(message.value, message.value_type) + '\n' +
|
||||
'context->SetWebsiteSetting("' + message.requesting_url + '", "' + message.top_level_url +
|
||||
'", '+ content_type +', value);\n';
|
||||
}
|
||||
|
||||
return makeDetails('C++ Code Example ' + makeCopyLink(example_id), 'cat_header_2', code, 'cat_body', example_id, false);
|
||||
}
|
||||
|
||||
var message_ct = 0;
|
||||
|
||||
// A new message has arrived. It may be queued, filtered out or displayed.
|
||||
function onSubscriptionMessage(message) {
|
||||
if (paused) {
|
||||
// Queue the message until the user clicks Resume.
|
||||
message.timestamp = getNiceTimestamp();
|
||||
paused_messages.push(message);
|
||||
document.getElementById("pause_button").value = 'Resume (' + paused_messages.length + ')';
|
||||
return;
|
||||
}
|
||||
|
||||
if (!passesFilter(message)) {
|
||||
// Filter out the message.
|
||||
filtered_ct++;
|
||||
document.getElementById("filtered_ct").innerHTML = filtered_ct;
|
||||
return;
|
||||
}
|
||||
|
||||
// Use the arrival timestamp for queued messages.
|
||||
var timestamp;
|
||||
if (message.timestamp) {
|
||||
timestamp = message.timestamp;
|
||||
delete message.timestamp;
|
||||
} else {
|
||||
timestamp = getNiceTimestamp();
|
||||
}
|
||||
|
||||
// Display the message.
|
||||
var label = timestamp + ': ';
|
||||
var content = 'value_type=' + getValueType(message.value_type);
|
||||
var search = '';
|
||||
var filter = '';
|
||||
|
||||
if (message.type === "preference") {
|
||||
label += 'Preference (' + (message.global ? 'Global' : 'Profile') +
|
||||
') <span class="mono">' + message.name + '</span>';
|
||||
search = '%5C%22' + message.name + '%5C%22';
|
||||
filter = "'preference', '" + message.name + "', " + (message.global ? 'true' : 'false');
|
||||
} else if (message.type === "setting") {
|
||||
label += 'Setting <span class="mono">' + getSettingTypeLabel(message.content_type) + '</span>';
|
||||
const setting_type = getSettingType(message.content_type);
|
||||
search = 'ContentSettingsType::' + setting_type;
|
||||
filter = "'setting', '" + setting_type + "'";
|
||||
content = 'requesting_url=' + message.requesting_url +
|
||||
'\ntop_level_url=' + message.top_level_url +
|
||||
'\n' + content;
|
||||
}
|
||||
content += '\nvalue=' + JSON.stringify(message.value, null, 1);
|
||||
label += ' <a href="#" onMouseDown="doFilter(' + filter + ')" onClick="return false">[filter]</a>' +
|
||||
' <a href="https://source.chromium.org/search?q=' + search + '" target="_blank">[search 🔗]</a>';
|
||||
|
||||
const messages = document.getElementById('messages');
|
||||
|
||||
if (first_after_pause) {
|
||||
messages.prepend(makeHR('RESUMED'));
|
||||
first_after_pause = false;
|
||||
}
|
||||
|
||||
const elem_id = message_ct++;
|
||||
const newDetails = makeDetails(label, null, makeContent(elem_id, content).outerHTML +
|
||||
makeCodeExample(elem_id, message).outerHTML, 'cat_body');
|
||||
messages.prepend(newDetails);
|
||||
}
|
||||
|
||||
// Clear filter count and displayed/pending messages.
|
||||
function doClear() {
|
||||
filtered_ct = 0;
|
||||
document.getElementById("filtered_ct").textContent = 0;
|
||||
document.getElementById('messages').innerHTML = '';
|
||||
if (paused) {
|
||||
paused_messages = [];
|
||||
document.getElementById("pause_button").value = 'Resume';
|
||||
}
|
||||
message_ct = 0;
|
||||
}
|
||||
|
||||
function showTempMessage(msg) {
|
||||
const element = document.getElementById("temp-message");
|
||||
element.innerHTML = msg;
|
||||
element.style.display = "block";
|
||||
|
||||
setTimeout(function() {
|
||||
element.style.display = "none";
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
function copyToClipboard(elementId) {
|
||||
const element = document.getElementById(elementId);
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Make all parent details nodes are open, otherwise nothing will be copied to the clipboard.
|
||||
var parent = element.parentNode;
|
||||
while (parent) {
|
||||
if (parent.nodeName === 'DETAILS') {
|
||||
if (!parent.open) {
|
||||
parent.open = true;
|
||||
}
|
||||
}
|
||||
parent = parent.parentNode;
|
||||
}
|
||||
|
||||
navigator.clipboard.writeText(element.outerText)
|
||||
.then(() => {
|
||||
showTempMessage('Text copied to clipboard.');
|
||||
})
|
||||
.catch(err => {
|
||||
showTempMessage('Failed to copy text to clipboard!');
|
||||
console.error('Failed to copy text: ', err);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body bgcolor="white" onload="onLoad()" onunload="onUnload()">
|
||||
<div id="message"></div>
|
||||
<details open>
|
||||
<summary class="cat_header_0">Startup configuration</summary>
|
||||
<p class="desc">
|
||||
This section displays the global configuration (Chrome Variations) that was applied at startup.
|
||||
Chrome Variations can be configured via chrome://flags <sup>[*]</sup>, via the below command-line switches, or via field trials (disabled in Official builds).
|
||||
The Active Variations section below is the human-readable equivalent of the "Active Variations" section of chrome://version.
|
||||
See <a href="https://developer.chrome.com/docs/web-platform/chrome-variations" target="_blank">Chrome Variations docs</a> for background.
|
||||
</p>
|
||||
<p class="foot">
|
||||
* Flags are stored in the global <span class="mono">browser.enabled_labs_experiments</span> preference.
|
||||
</p>
|
||||
<p class="cat_header_1">Command-Line Switches:</p>
|
||||
<p class="cat_body" id="global_switches"></p>
|
||||
<details>
|
||||
<summary class="cat_header_1">Active Variations (<span id="global_strings_ct">0</span>)</summary>
|
||||
<p class="cat_body" id="global_strings"></p>
|
||||
</details>
|
||||
</details>
|
||||
<br/>
|
||||
<details open>
|
||||
<summary class="cat_header_0">Runtime configuration</summary>
|
||||
<p class="desc">
|
||||
This section displays preference and site settings changes that occur during runtime.
|
||||
Chromium stores both global and Profile-specific preferences.
|
||||
See <a href="https://www.chromium.org/developers/design-documents/preferences/" target="_blank">Preferences docs</a> for background.
|
||||
To view a snapshot of all preferences go <a href="https://tests/preferences#advanced">here</a> instead.
|
||||
</p>
|
||||
<p id="filter">
|
||||
<form id="form">
|
||||
Text Contains: <input type="text" id="filter_text"/> <input type="button" onclick="updateFilter();" value="Apply"/>
|
||||
<br/>Show: <input type="checkbox" id="filter_global_prefs" onChange="updateFilter()" checked /> Global preferences
|
||||
<input type="checkbox" id="filter_context_prefs" onChange="updateFilter()" checked /> Profile-specific preferences
|
||||
<input type="checkbox" id="filter_context_settings" onChange="updateFilter()" checked /> Site settings <sup>[*]</sup>
|
||||
<br/><input type="button" id="clear_button" onclick="doClear()" value="Clear"/>
|
||||
<input type="button" id="pause_button" onclick="togglePause()" value="Pause"/> <sup>[**]</sup> Filtered out: <span id="filtered_ct">0</span>
|
||||
<input type="button" id="reset_button" onclick="doFilterReset()" value="Reset"/>
|
||||
<p class="foot">
|
||||
* Site settings are stored in the Profile-specific <span class="mono">profile.content_settings</span> preference and can be modified via chrome://settings/content.
|
||||
<br/>** Events will not be displayed or filtered out while processing is paused.
|
||||
</p>
|
||||
</form>
|
||||
</p>
|
||||
<div id="messages" onMouseDown="doPause()"></div>
|
||||
<div id="temp-message"></div>
|
||||
</details>
|
||||
</body>
|
||||
</html>
|
@@ -10,6 +10,7 @@
|
||||
<li><a href="https://jigsaw.w3.org/HTTP/Basic/">Authentication (Basic)</a> - credentials returned via GetAuthCredentials</li>
|
||||
<li><a href="https://jigsaw.w3.org/HTTP/Digest/">Authentication (Digest)</a> - credentials returned via GetAuthCredentials</li>
|
||||
<li><a href="binary_transfer">Binary vs String Transfer Benchmark</a></li>
|
||||
<li><a href="config">Chrome Configuration</a></li>
|
||||
<li><a href="http://html5advent2011.digitpaint.nl/3/index.html">Cursors</a></li>
|
||||
<li><a href="dialogs">Dialogs</a></li>
|
||||
<li><a href="http://html5demos.com/drag">Drag & Drop</a></li>
|
||||
|
@@ -13,8 +13,12 @@
|
||||
|
||||
<script>
|
||||
function setup() {
|
||||
if (location.hostname == 'tests' || location.hostname == 'localhost')
|
||||
if (location.hostname === 'tests' || location.hostname === 'localhost') {
|
||||
if (location.hash === '#advanced') {
|
||||
toggleView();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
alert('This page can only be run from tests or localhost.');
|
||||
|
||||
|
@@ -31,6 +31,7 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
|
||||
IDS_BINARY_TRANSFER_HTML BINARY "tests\\cefclient\\resources\\binary_transfer.html"
|
||||
IDS_BINDING_HTML BINARY "tests\\cefclient\\resources\\binding.html"
|
||||
IDS_CONFIG_HTML BINARY "tests\\cefclient\\resources\\config.html"
|
||||
IDS_DIALOGS_HTML BINARY "tests\\cefclient\\resources\\dialogs.html"
|
||||
IDS_DRAGGABLE_HTML BINARY "tests\\cefclient\\resources\\draggable.html"
|
||||
IDS_HANG_HTML BINARY "tests\\cefclient\\resources\\hang.html"
|
||||
|
Reference in New Issue
Block a user