// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/chrome/browser/webui/ui_bundled/userdefaults_internals_ui.h"
#import <Foundation/Foundation.h>
#import "base/apple/foundation_util.h"
#import "base/memory/ref_counted_memory.h"
#import "base/strings/sys_string_conversions.h"
#import "ios/chrome/browser/shared/model/profile/profile_ios.h"
#import "ios/chrome/browser/shared/model/url/chrome_url_constants.h"
#import "ios/web/public/thread/web_thread.h"
#import "ios/web/public/webui/url_data_source_ios.h"
#import "ios/web/public/webui/web_ui_ios.h"
namespace {
// A simple data source that returns the user defaults preferences.
class UserDefaultsInternalsSource : public web::URLDataSourceIOS {
public:
explicit UserDefaultsInternalsSource(bool is_incognito)
: is_incognito_(is_incognito) {}
UserDefaultsInternalsSource(const UserDefaultsInternalsSource&) = delete;
UserDefaultsInternalsSource& operator=(const UserDefaultsInternalsSource&) =
delete;
~UserDefaultsInternalsSource() override = default;
// web::URLDataSourceIOS
std::string GetSource() const override {
return kChromeUIUserDefaultsInternalsHost;
}
std::string GetMimeType(const std::string& path) const override {
return "text/plain";
}
void StartDataRequest(
const std::string& path,
web::URLDataSourceIOS::GotDataCallback callback) override {
std::string response;
if (is_incognito_) {
response.append("This page is not available in incognito mode.");
FinishDataRequest(std::move(response), std::move(callback));
return;
}
response.append("<!DOCTYPE HTML>\n<html>\n<head>\n");
response.append(
"<style>table, th, td {border: 1px solid black; "
"border-collapse: collapse;}th {text-align: left;}</style>");
response.append("<h1>List of user defaults:</h1>\n\n");
NSDictionary<NSString*, id>* defaultsDict =
[[NSUserDefaults standardUserDefaults] dictionaryRepresentation];
NSArray* sortedKeys = [[defaultsDict allKeys]
sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)];
response.append("<table>\n");
for (NSString* key in sortedKeys) {
id value = defaultsDict[key];
NSString* valueString;
if ([value isKindOfClass:[NSArray class]]) {
valueString = [value description];
} else if ([value isKindOfClass:[NSDictionary class]]) {
valueString = [value description];
} else if ([value isKindOfClass:[NSData class]]) {
valueString = [value description];
} else if ([value isKindOfClass:[NSString class]]) {
valueString = base::apple::ObjCCastStrict<NSString>(value);
} else if ([value isKindOfClass:[NSNumber class]]) {
valueString =
[base::apple::ObjCCastStrict<NSNumber>(value) stringValue];
} else if ([value isKindOfClass:[NSDate class]]) {
valueString = [value description];
}
response.append("<tr>\n");
response.append("<th>" + base::SysNSStringToUTF8(key) + "</th>\n");
response.append("<td>" + base::SysNSStringToUTF8(valueString) +
"</td>\n");
response.append("</tr>\n");
}
response.append("</table>\n<html>");
FinishDataRequest(std::move(response), std::move(callback));
}
void FinishDataRequest(std::string html,
web::URLDataSourceIOS::GotDataCallback callback) {
std::move(callback).Run(
base::MakeRefCounted<base::RefCountedString>(std::move(html)));
}
private:
bool is_incognito_;
};
} // anonymous namespace
UserDefaultsInternalsUI::UserDefaultsInternalsUI(web::WebUIIOS* web_ui,
const std::string& host)
: web::WebUIIOSController(web_ui, host) {
ChromeBrowserState* browser_state = ChromeBrowserState::FromWebUIIOS(web_ui);
web::URLDataSourceIOS::Add(
browser_state,
new UserDefaultsInternalsSource(browser_state->IsOffTheRecord()));
}
UserDefaultsInternalsUI::~UserDefaultsInternalsUI() = default;