blob: ea4edf81c14cc77b797b21c1c7959f84c854453a [file] [log] [blame]
// Copyright 2015 The Chromium 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 "ios/chrome/browser/ui/webui/about_ui.h"
#include <algorithm>
#include <string>
#include <utility>
#include <vector>
#include "base/format_macros.h"
#include "base/i18n/number_formatting.h"
#include "base/macros.h"
#include "base/metrics/statistics_recorder.h"
#include "base/strings/string_number_conversions.h"
#include "components/grit/components_resources.h"
#include "google_apis/gaia/google_service_auth_error.h"
#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
#include "ios/chrome/browser/chrome_url_constants.h"
#include "ios/web/public/url_data_source_ios.h"
#include "net/base/escape.h"
#include "third_party/brotli/include/brotli/decode.h"
#include "ui/base/device_form_factor.h"
#include "ui/base/resource/resource_bundle.h"
#include "url/gurl.h"
namespace {
const char kCreditsJsPath[] = "credits.js";
const char kStringsJsPath[] = "strings.js";
class AboutUIHTMLSource : public web::URLDataSourceIOS {
public:
// Construct a data source for the specified |source_name|.
explicit AboutUIHTMLSource(const std::string& source_name);
// web::URLDataSourceIOS implementation.
std::string GetSource() const override;
void StartDataRequest(
const std::string& path,
const web::URLDataSourceIOS::GotDataCallback& callback) override;
std::string GetMimeType(const std::string& path) const override;
bool ShouldDenyXFrameOptions() const override;
// Send the response data.
void FinishDataRequest(
const std::string& html,
const web::URLDataSourceIOS::GotDataCallback& callback);
private:
~AboutUIHTMLSource() override;
std::string source_name_;
DISALLOW_COPY_AND_ASSIGN(AboutUIHTMLSource);
};
void AppendHeader(std::string* output,
int refresh,
const std::string& unescaped_title) {
output->append("<!DOCTYPE HTML>\n<html>\n<head>\n");
if (!unescaped_title.empty()) {
output->append("<title>");
output->append(net::EscapeForHTML(unescaped_title));
output->append("</title>\n");
}
output->append("<meta charset='utf-8'>\n");
if (refresh > 0) {
output->append("<meta http-equiv='refresh' content='");
output->append(base::IntToString(refresh));
output->append("'/>\n");
}
}
void AppendBody(std::string* output) {
output->append("</head>\n<body>\n");
}
void AppendFooter(std::string* output) {
output->append("</body>\n</html>\n");
}
std::string ChromeURLs() {
std::string html;
AppendHeader(&html, 0, "Chrome URLs");
AppendBody(&html);
html += "<h2>List of Chrome URLs</h2>\n<ul>\n";
std::vector<std::string> hosts(kChromeHostURLs,
kChromeHostURLs + kNumberOfChromeHostURLs);
std::sort(hosts.begin(), hosts.end());
for (std::vector<std::string>::const_iterator i = hosts.begin();
i != hosts.end(); ++i)
html += "<li><a href='chrome://" + *i + "/' id='" + *i + "'>chrome://" +
*i + "</a></li>\n";
html += "</ul>\n";
AppendFooter(&html);
return html;
}
} // namespace
// AboutUIHTMLSource ----------------------------------------------------------
AboutUIHTMLSource::AboutUIHTMLSource(const std::string& source_name)
: source_name_(source_name) {}
AboutUIHTMLSource::~AboutUIHTMLSource() {}
std::string AboutUIHTMLSource::GetSource() const {
return source_name_;
}
void AboutUIHTMLSource::StartDataRequest(
const std::string& path,
const web::URLDataSourceIOS::GotDataCallback& callback) {
std::string response;
// Add your data source here, in alphabetical order.
if (source_name_ == kChromeUIChromeURLsHost) {
response = ChromeURLs();
} else if (source_name_ == kChromeUICreditsHost) {
int idr = IDR_ABOUT_UI_CREDITS_HTML;
if (path == kCreditsJsPath)
idr = IDR_ABOUT_UI_CREDITS_JS;
base::StringPiece raw_response =
ui::ResourceBundle::GetSharedInstance().GetRawDataResource(idr);
if (idr == IDR_ABOUT_UI_CREDITS_HTML) {
const uint8_t* next_encoded_byte =
reinterpret_cast<const uint8_t*>(raw_response.data());
size_t input_size_remaining = raw_response.size();
BrotliDecoderState* decoder =
BrotliDecoderCreateInstance(nullptr /* no custom allocator */,
nullptr /* no custom deallocator */,
nullptr /* no custom memory handle */);
CHECK(!!decoder);
while (!BrotliDecoderIsFinished(decoder)) {
size_t output_size_remaining = 0;
CHECK(BrotliDecoderDecompressStream(
decoder, &input_size_remaining, &next_encoded_byte,
&output_size_remaining, nullptr,
nullptr) != BROTLI_DECODER_RESULT_ERROR);
const uint8_t* output_buffer =
BrotliDecoderTakeOutput(decoder, &output_size_remaining);
response.insert(response.end(), output_buffer,
output_buffer + output_size_remaining);
}
BrotliDecoderDestroyInstance(decoder);
} else {
response = raw_response.as_string();
}
} else if (source_name_ == kChromeUIHistogramHost) {
// Note: On other platforms, this is implemented in //content. If there is
// ever a need for embedders other than //ios/chrome to use
// chrome://histograms, this code could likely be moved to //io/web.
base::StatisticsRecorder::WriteHTMLGraph("", &response);
}
FinishDataRequest(response, callback);
}
void AboutUIHTMLSource::FinishDataRequest(
const std::string& html,
const web::URLDataSourceIOS::GotDataCallback& callback) {
std::string html_copy(html);
callback.Run(base::RefCountedString::TakeString(&html_copy));
}
std::string AboutUIHTMLSource::GetMimeType(const std::string& path) const {
if (path == kCreditsJsPath || path == kStringsJsPath) {
return "application/javascript";
}
return "text/html";
}
bool AboutUIHTMLSource::ShouldDenyXFrameOptions() const {
return web::URLDataSourceIOS::ShouldDenyXFrameOptions();
}
AboutUI::AboutUI(web::WebUIIOS* web_ui, const std::string& name)
: web::WebUIIOSController(web_ui) {
web::URLDataSourceIOS::Add(ios::ChromeBrowserState::FromWebUIIOS(web_ui),
new AboutUIHTMLSource(name));
}
AboutUI::~AboutUI() {}