1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "ui/base/webui/web_ui_util.h"
6 
7 #include <vector>
8 
9 #include "base/base64.h"
10 #include "base/feature_list.h"
11 #include "base/i18n/rtl.h"
12 #include "base/logging.h"
13 #include "base/memory/ref_counted_memory.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/string_piece.h"
16 #include "base/strings/string_util.h"
17 #include "base/trace_event/trace_event.h"
18 #include "build/build_config.h"
19 #include "net/base/escape.h"
20 #include "third_party/modp_b64/modp_b64.h"
21 #include "ui/base/l10n/l10n_util.h"
22 #include "ui/base/resource/resource_bundle.h"
23 #include "ui/base/template_expressions.h"
24 #include "ui/base/window_open_disposition.h"
25 #include "ui/gfx/codec/png_codec.h"
26 #include "ui/gfx/font.h"
27 #include "ui/gfx/image/image_skia.h"
28 #include "ui/resources/grit/webui_generated_resources.h"
29 #include "ui/strings/grit/app_locale_settings.h"
30 #include "url/gurl.h"
31 
32 #if defined(OS_WIN)
33 #include "base/win/windows_version.h"
34 #endif
35 
36 namespace webui {
37 namespace {
GetWebUiCssTextDefaults(const std::string & css_template)38 std::string GetWebUiCssTextDefaults(const std::string& css_template) {
39   ui::TemplateReplacements placeholders;
40   placeholders["textDirection"] = GetTextDirection();
41   placeholders["fontFamily"] = GetFontFamily();
42   placeholders["fontSize"] = GetFontSize();
43   return ui::ReplaceTemplateExpressions(css_template, placeholders);
44 }
45 }  // namespace
46 
GetBitmapDataUrl(const SkBitmap & bitmap)47 std::string GetBitmapDataUrl(const SkBitmap& bitmap) {
48   TRACE_EVENT2("ui", "GetBitmapDataUrl", "width", bitmap.width(), "height",
49                bitmap.height());
50   std::vector<unsigned char> output;
51   gfx::PNGCodec::EncodeBGRASkBitmap(bitmap, false, &output);
52   return GetPngDataUrl(output.data(), output.size());
53 }
54 
GetPngDataUrl(const unsigned char * data,size_t size)55 std::string GetPngDataUrl(const unsigned char* data, size_t size) {
56   constexpr char kPrefix[] = "data:image/png;base64,";
57   constexpr size_t kPrefixLen = base::size(kPrefix) - 1;
58   // Includes room for trailing null byte.
59   size_t max_encode_len = modp_b64_encode_len(size);
60   std::string output;
61   // This initializes the characters in the string, but there's no good way to
62   // avoid that and maintain a std::string API.
63   output.resize(kPrefixLen + max_encode_len);
64   memcpy(&output[0], kPrefix, kPrefixLen);
65   // |max_encode_len| is >= 1, so &output[kPrefixLen] is valid.
66   size_t actual_encode_len = modp_b64_encode(
67       &output[kPrefixLen], reinterpret_cast<const char*>(data), size);
68   output.resize(kPrefixLen + actual_encode_len);
69   return output;
70 }
71 
GetDispositionFromClick(const base::ListValue * args,int start_index)72 WindowOpenDisposition GetDispositionFromClick(const base::ListValue* args,
73                                               int start_index) {
74   double button = 0.0;
75   bool alt_key = false;
76   bool ctrl_key = false;
77   bool meta_key = false;
78   bool shift_key = false;
79 
80   CHECK(args->GetDouble(start_index++, &button));
81   CHECK(args->GetBoolean(start_index++, &alt_key));
82   CHECK(args->GetBoolean(start_index++, &ctrl_key));
83   CHECK(args->GetBoolean(start_index++, &meta_key));
84   CHECK(args->GetBoolean(start_index++, &shift_key));
85   return ui::DispositionFromClick(
86       button == 1.0, alt_key, ctrl_key, meta_key, shift_key);
87 }
88 
ParseScaleFactor(const base::StringPiece & identifier,float * scale_factor)89 bool ParseScaleFactor(const base::StringPiece& identifier,
90                       float* scale_factor) {
91   *scale_factor = 1.0f;
92   if (identifier.empty()) {
93     LOG(WARNING) << "Invalid scale factor format: " << identifier;
94     return false;
95   }
96 
97   if (*identifier.rbegin() != 'x') {
98     LOG(WARNING) << "Invalid scale factor format: " << identifier;
99     return false;
100   }
101 
102   double scale = 0;
103   std::string stripped(identifier.substr(0, identifier.length() - 1));
104   if (!base::StringToDouble(stripped, &scale)) {
105     LOG(WARNING) << "Invalid scale factor format: " << identifier;
106     return false;
107   }
108   *scale_factor = static_cast<float>(scale);
109   return true;
110 }
111 
112 // Parse a formatted frame index string into int and sets to |frame_index|.
ParseFrameIndex(const base::StringPiece & identifier,int * frame_index)113 bool ParseFrameIndex(const base::StringPiece& identifier, int* frame_index) {
114   *frame_index = -1;
115   if (identifier.empty()) {
116     LOG(WARNING) << "Invalid frame index format: " << identifier;
117     return false;
118   }
119 
120   if (*identifier.rbegin() != ']') {
121     LOG(WARNING) << "Invalid frame index format: " << identifier;
122     return false;
123   }
124 
125   unsigned frame = 0;
126   if (!base::StringToUint(identifier.substr(0, identifier.length() - 1),
127                           &frame)) {
128     LOG(WARNING) << "Invalid frame index format: " << identifier;
129     return false;
130   }
131   *frame_index = static_cast<int>(frame);
132   return true;
133 }
134 
ParsePathAndImageSpec(const GURL & url,std::string * path,float * scale_factor,int * frame_index)135 void ParsePathAndImageSpec(const GURL& url,
136                            std::string* path,
137                            float* scale_factor,
138                            int* frame_index) {
139   *path = net::UnescapeBinaryURLComponent(url.path_piece().substr(1));
140   if (scale_factor)
141     *scale_factor = 1.0f;
142   if (frame_index)
143     *frame_index = -1;
144 
145   // Detect and parse resource string ending in @<scale>x.
146   std::size_t pos = path->rfind('@');
147   if (pos != std::string::npos) {
148     base::StringPiece stripped_path(*path);
149     float factor;
150 
151     if (ParseScaleFactor(stripped_path.substr(
152             pos + 1, stripped_path.length() - pos - 1), &factor)) {
153       // Strip scale factor specification from path.
154       stripped_path.remove_suffix(stripped_path.length() - pos);
155       path->assign(stripped_path.data(), stripped_path.size());
156     }
157     if (scale_factor)
158       *scale_factor = factor;
159   }
160 
161   // Detect and parse resource string ending in [<frame>].
162   pos = path->rfind('[');
163   if (pos != std::string::npos) {
164     base::StringPiece stripped_path(*path);
165     int index;
166 
167     if (ParseFrameIndex(
168             stripped_path.substr(pos + 1, stripped_path.length() - pos - 1),
169             &index)) {
170       // Strip frame index specification from path.
171       stripped_path.remove_suffix(stripped_path.length() - pos);
172       path->assign(stripped_path.data(), stripped_path.size());
173     }
174     if (frame_index)
175       *frame_index = index;
176   }
177 }
178 
ParsePathAndScale(const GURL & url,std::string * path,float * scale_factor)179 void ParsePathAndScale(const GURL& url,
180                        std::string* path,
181                        float* scale_factor) {
182   ParsePathAndImageSpec(url, path, scale_factor, nullptr);
183 }
184 
SetLoadTimeDataDefaults(const std::string & app_locale,base::DictionaryValue * localized_strings)185 void SetLoadTimeDataDefaults(const std::string& app_locale,
186                              base::DictionaryValue* localized_strings) {
187   localized_strings->SetString("fontfamily", GetFontFamily());
188   localized_strings->SetString("fontsize", GetFontSize());
189   localized_strings->SetString("language", l10n_util::GetLanguage(app_locale));
190   localized_strings->SetString("textdirection", GetTextDirection());
191 }
192 
SetLoadTimeDataDefaults(const std::string & app_locale,ui::TemplateReplacements * replacements)193 void SetLoadTimeDataDefaults(const std::string& app_locale,
194                              ui::TemplateReplacements* replacements) {
195   (*replacements)["fontfamily"] = GetFontFamily();
196   (*replacements)["fontsize"] = GetFontSize();
197   (*replacements)["language"] = l10n_util::GetLanguage(app_locale);
198   (*replacements)["textdirection"] = GetTextDirection();
199 }
200 
GetWebUiCssTextDefaults()201 std::string GetWebUiCssTextDefaults() {
202   const ui::ResourceBundle& resource_bundle =
203       ui::ResourceBundle::GetSharedInstance();
204   return GetWebUiCssTextDefaults(
205       resource_bundle.LoadDataResourceString(IDR_WEBUI_CSS_TEXT_DEFAULTS_CSS));
206 }
207 
GetWebUiCssTextDefaultsMd()208 std::string GetWebUiCssTextDefaultsMd() {
209   const ui::ResourceBundle& resource_bundle =
210       ui::ResourceBundle::GetSharedInstance();
211   return GetWebUiCssTextDefaults(resource_bundle.LoadDataResourceString(
212       IDR_WEBUI_CSS_TEXT_DEFAULTS_MD_CSS));
213 }
214 
AppendWebUiCssTextDefaults(std::string * html)215 void AppendWebUiCssTextDefaults(std::string* html) {
216   html->append("<style>");
217   html->append(GetWebUiCssTextDefaults());
218   html->append("</style>");
219 }
220 
GetFontFamily()221 std::string GetFontFamily() {
222   std::string font_family = l10n_util::GetStringUTF8(IDS_WEB_FONT_FAMILY);
223 
224 #if (defined(OS_LINUX) && !defined(OS_CHROMEOS)) || defined(OS_BSD)
225   std::string font_name = ui::ResourceBundle::GetSharedInstance()
226                               .GetFont(ui::ResourceBundle::BaseFont)
227                               .GetFontName();
228   // Wrap |font_name| with quotes to ensure it will always be parsed correctly
229   // in CSS.
230   font_family = "\"" + font_name + "\", " + font_family;
231 #endif
232 
233   return font_family;
234 }
235 
GetFontSize()236 std::string GetFontSize() {
237   return l10n_util::GetStringUTF8(IDS_WEB_FONT_SIZE);
238 }
239 
GetTextDirection()240 std::string GetTextDirection() {
241   return base::i18n::IsRTL() ? "rtl" : "ltr";
242 }
243 
244 }  // namespace webui
245