1 // Copyright (c) 2012 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 "chrome/renderer/pepper/pepper_flash_renderer_host.h" 6 7 #include <map> 8 #include <vector> 9 10 #include "base/lazy_instance.h" 11 #include "base/metrics/histogram.h" 12 #include "base/strings/string_util.h" 13 #include "components/pdf/renderer/ppb_pdf_impl.h" 14 #include "content/public/renderer/pepper_plugin_instance.h" 15 #include "content/public/renderer/render_thread.h" 16 #include "content/public/renderer/renderer_ppapi_host.h" 17 #include "ipc/ipc_message_macros.h" 18 #include "net/http/http_util.h" 19 #include "ppapi/c/pp_errors.h" 20 #include "ppapi/c/trusted/ppb_browser_font_trusted.h" 21 #include "ppapi/host/dispatch_host_message.h" 22 #include "ppapi/proxy/host_dispatcher.h" 23 #include "ppapi/proxy/ppapi_messages.h" 24 #include "ppapi/proxy/resource_message_params.h" 25 #include "ppapi/proxy/serialized_structs.h" 26 #include "ppapi/thunk/enter.h" 27 #include "ppapi/thunk/ppb_image_data_api.h" 28 #include "skia/ext/platform_canvas.h" 29 #include "third_party/skia/include/core/SkCanvas.h" 30 #include "third_party/skia/include/core/SkMatrix.h" 31 #include "third_party/skia/include/core/SkPaint.h" 32 #include "third_party/skia/include/core/SkPoint.h" 33 #include "third_party/skia/include/core/SkTemplates.h" 34 #include "third_party/skia/include/core/SkTypeface.h" 35 #include "ui/gfx/rect.h" 36 #include "url/gurl.h" 37 38 using ppapi::thunk::EnterResourceNoLock; 39 using ppapi::thunk::PPB_ImageData_API; 40 41 namespace { 42 43 // Some non-simple HTTP request headers that Flash may set. 44 // (Please see http://www.w3.org/TR/cors/#simple-header for the definition of 45 // simple headers.) 46 // 47 // The list and the enum defined below are used to collect data about request 48 // headers used in PPB_Flash.Navigate() calls, in order to understand the impact 49 // of rejecting PPB_Flash.Navigate() requests with non-simple headers. 50 // 51 // TODO(yzshen): We should be able to remove the histogram recording code once 52 // we get the answer. 53 const char* kRejectedHttpRequestHeaders[] = { 54 "authorization", // 55 "cache-control", // 56 "content-encoding", // 57 "content-md5", // 58 "content-type", // If the media type is not one of those covered by the 59 // simple header definition. 60 "expires", // 61 "from", // 62 "if-match", // 63 "if-none-match", // 64 "if-range", // 65 "if-unmodified-since", // 66 "pragma", // 67 "referer" // 68 }; 69 70 // Please note that new entries should be added right above 71 // FLASH_NAVIGATE_USAGE_ENUM_COUNT, and existing entries shouldn't be re-ordered 72 // or removed, since this ordering is used in a histogram. 73 enum FlashNavigateUsage { 74 // This section must be in the same order as kRejectedHttpRequestHeaders. 75 REJECT_AUTHORIZATION = 0, 76 REJECT_CACHE_CONTROL, 77 REJECT_CONTENT_ENCODING, 78 REJECT_CONTENT_MD5, 79 REJECT_CONTENT_TYPE, 80 REJECT_EXPIRES, 81 REJECT_FROM, 82 REJECT_IF_MATCH, 83 REJECT_IF_NONE_MATCH, 84 REJECT_IF_RANGE, 85 REJECT_IF_UNMODIFIED_SINCE, 86 REJECT_PRAGMA, 87 REJECT_REFERER, 88 89 // The navigate request is rejected because of headers not listed above 90 // (e.g., custom headers). 91 REJECT_OTHER_HEADERS, 92 93 // Total number of rejected navigate requests. 94 TOTAL_REJECTED_NAVIGATE_REQUESTS, 95 96 // Total number of navigate requests. 97 TOTAL_NAVIGATE_REQUESTS, 98 FLASH_NAVIGATE_USAGE_ENUM_COUNT 99 }; 100 101 static base::LazyInstance<std::map<std::string, FlashNavigateUsage> > 102 g_rejected_headers = LAZY_INSTANCE_INITIALIZER; 103 104 bool IsSimpleHeader(const std::string& lower_case_header_name, 105 const std::string& header_value) { 106 if (lower_case_header_name == "accept" || 107 lower_case_header_name == "accept-language" || 108 lower_case_header_name == "content-language") { 109 return true; 110 } 111 112 if (lower_case_header_name == "content-type") { 113 std::string lower_case_mime_type; 114 std::string lower_case_charset; 115 bool had_charset = false; 116 net::HttpUtil::ParseContentType(header_value, 117 &lower_case_mime_type, 118 &lower_case_charset, 119 &had_charset, 120 NULL); 121 return lower_case_mime_type == "application/x-www-form-urlencoded" || 122 lower_case_mime_type == "multipart/form-data" || 123 lower_case_mime_type == "text/plain"; 124 } 125 126 return false; 127 } 128 129 void RecordFlashNavigateUsage(FlashNavigateUsage usage) { 130 DCHECK_NE(FLASH_NAVIGATE_USAGE_ENUM_COUNT, usage); 131 UMA_HISTOGRAM_ENUMERATION( 132 "Plugin.FlashNavigateUsage", usage, FLASH_NAVIGATE_USAGE_ENUM_COUNT); 133 } 134 135 } // namespace 136 137 PepperFlashRendererHost::PepperFlashRendererHost( 138 content::RendererPpapiHost* host, 139 PP_Instance instance, 140 PP_Resource resource) 141 : ResourceHost(host->GetPpapiHost(), instance, resource), 142 host_(host), 143 weak_factory_(this) {} 144 145 PepperFlashRendererHost::~PepperFlashRendererHost() { 146 // This object may be destroyed in the middle of a sync message. If that is 147 // the case, make sure we respond to all the pending navigate calls. 148 std::vector<ppapi::host::ReplyMessageContext>::reverse_iterator it; 149 for (it = navigate_replies_.rbegin(); it != navigate_replies_.rend(); ++it) 150 SendReply(*it, IPC::Message()); 151 } 152 153 int32_t PepperFlashRendererHost::OnResourceMessageReceived( 154 const IPC::Message& msg, 155 ppapi::host::HostMessageContext* context) { 156 PPAPI_BEGIN_MESSAGE_MAP(PepperFlashRendererHost, msg) 157 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_Flash_GetProxyForURL, 158 OnGetProxyForURL) 159 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_Flash_SetInstanceAlwaysOnTop, 160 OnSetInstanceAlwaysOnTop) 161 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_Flash_DrawGlyphs, 162 OnDrawGlyphs) 163 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_Flash_Navigate, OnNavigate) 164 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_Flash_IsRectTopmost, 165 OnIsRectTopmost) 166 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_Flash_InvokePrinting, 167 OnInvokePrinting) 168 PPAPI_END_MESSAGE_MAP() 169 return PP_ERROR_FAILED; 170 } 171 172 int32_t PepperFlashRendererHost::OnGetProxyForURL( 173 ppapi::host::HostMessageContext* host_context, 174 const std::string& url) { 175 GURL gurl(url); 176 if (!gurl.is_valid()) 177 return PP_ERROR_FAILED; 178 std::string proxy; 179 bool result = content::RenderThread::Get()->ResolveProxy(gurl, &proxy); 180 if (!result) 181 return PP_ERROR_FAILED; 182 host_context->reply_msg = PpapiPluginMsg_Flash_GetProxyForURLReply(proxy); 183 return PP_OK; 184 } 185 186 int32_t PepperFlashRendererHost::OnSetInstanceAlwaysOnTop( 187 ppapi::host::HostMessageContext* host_context, 188 bool on_top) { 189 content::PepperPluginInstance* plugin_instance = 190 host_->GetPluginInstance(pp_instance()); 191 if (plugin_instance) 192 plugin_instance->SetAlwaysOnTop(on_top); 193 // Since no reply is sent for this message, it doesn't make sense to return an 194 // error. 195 return PP_OK; 196 } 197 198 int32_t PepperFlashRendererHost::OnDrawGlyphs( 199 ppapi::host::HostMessageContext* host_context, 200 ppapi::proxy::PPBFlash_DrawGlyphs_Params params) { 201 if (params.glyph_indices.size() != params.glyph_advances.size() || 202 params.glyph_indices.empty()) 203 return PP_ERROR_FAILED; 204 205 // Set up the typeface. 206 int style = SkTypeface::kNormal; 207 if (static_cast<PP_BrowserFont_Trusted_Weight>(params.font_desc.weight) >= 208 PP_BROWSERFONT_TRUSTED_WEIGHT_BOLD) 209 style |= SkTypeface::kBold; 210 if (params.font_desc.italic) 211 style |= SkTypeface::kItalic; 212 skia::RefPtr<SkTypeface> typeface = skia::AdoptRef(SkTypeface::CreateFromName( 213 params.font_desc.face.c_str(), static_cast<SkTypeface::Style>(style))); 214 if (!typeface) 215 return PP_ERROR_FAILED; 216 217 EnterResourceNoLock<PPB_ImageData_API> enter( 218 params.image_data.host_resource(), true); 219 if (enter.failed()) 220 return PP_ERROR_FAILED; 221 222 // Set up the canvas. 223 PPB_ImageData_API* image = static_cast<PPB_ImageData_API*>(enter.object()); 224 SkCanvas* canvas = image->GetPlatformCanvas(); 225 bool needs_unmapping = false; 226 if (!canvas) { 227 needs_unmapping = true; 228 image->Map(); 229 canvas = image->GetPlatformCanvas(); 230 if (!canvas) 231 return PP_ERROR_FAILED; // Failure mapping. 232 } 233 234 SkAutoCanvasRestore acr(canvas, true); 235 236 // Clip is applied in pixels before the transform. 237 SkRect clip_rect = { 238 SkIntToScalar(params.clip.point.x), SkIntToScalar(params.clip.point.y), 239 SkIntToScalar(params.clip.point.x + params.clip.size.width), 240 SkIntToScalar(params.clip.point.y + params.clip.size.height)}; 241 canvas->clipRect(clip_rect); 242 243 // Convert & set the matrix. 244 SkMatrix matrix; 245 matrix.set(SkMatrix::kMScaleX, SkFloatToScalar(params.transformation[0][0])); 246 matrix.set(SkMatrix::kMSkewX, SkFloatToScalar(params.transformation[0][1])); 247 matrix.set(SkMatrix::kMTransX, SkFloatToScalar(params.transformation[0][2])); 248 matrix.set(SkMatrix::kMSkewY, SkFloatToScalar(params.transformation[1][0])); 249 matrix.set(SkMatrix::kMScaleY, SkFloatToScalar(params.transformation[1][1])); 250 matrix.set(SkMatrix::kMTransY, SkFloatToScalar(params.transformation[1][2])); 251 matrix.set(SkMatrix::kMPersp0, SkFloatToScalar(params.transformation[2][0])); 252 matrix.set(SkMatrix::kMPersp1, SkFloatToScalar(params.transformation[2][1])); 253 matrix.set(SkMatrix::kMPersp2, SkFloatToScalar(params.transformation[2][2])); 254 canvas->concat(matrix); 255 256 SkPaint paint; 257 paint.setColor(params.color); 258 paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); 259 paint.setAntiAlias(true); 260 paint.setHinting(SkPaint::kFull_Hinting); 261 paint.setTextSize(SkIntToScalar(params.font_desc.size)); 262 paint.setTypeface(typeface.get()); // Takes a ref and manages lifetime. 263 if (params.allow_subpixel_aa) { 264 paint.setSubpixelText(true); 265 paint.setLCDRenderText(true); 266 } 267 268 SkScalar x = SkIntToScalar(params.position.x); 269 SkScalar y = SkIntToScalar(params.position.y); 270 271 // Build up the skia advances. 272 size_t glyph_count = params.glyph_indices.size(); 273 if (glyph_count) { 274 std::vector<SkPoint> storage; 275 storage.resize(glyph_count); 276 SkPoint* sk_positions = &storage[0]; 277 for (uint32_t i = 0; i < glyph_count; i++) { 278 sk_positions[i].set(x, y); 279 x += SkFloatToScalar(params.glyph_advances[i].x); 280 y += SkFloatToScalar(params.glyph_advances[i].y); 281 } 282 283 canvas->drawPosText( 284 ¶ms.glyph_indices[0], glyph_count * 2, sk_positions, paint); 285 } 286 287 if (needs_unmapping) 288 image->Unmap(); 289 290 return PP_OK; 291 } 292 293 // CAUTION: This code is subtle because Navigate is a sync call which may 294 // cause re-entrancy or cause the instance to be destroyed. If the instance 295 // is destroyed we need to ensure that we respond to all outstanding sync 296 // messages so that the plugin process does not remain blocked. 297 int32_t PepperFlashRendererHost::OnNavigate( 298 ppapi::host::HostMessageContext* host_context, 299 const ppapi::URLRequestInfoData& data, 300 const std::string& target, 301 bool from_user_action) { 302 // If our PepperPluginInstance is already destroyed, just return a failure. 303 content::PepperPluginInstance* plugin_instance = 304 host_->GetPluginInstance(pp_instance()); 305 if (!plugin_instance) 306 return PP_ERROR_FAILED; 307 308 std::map<std::string, FlashNavigateUsage>& rejected_headers = 309 g_rejected_headers.Get(); 310 if (rejected_headers.empty()) { 311 for (size_t i = 0; i < arraysize(kRejectedHttpRequestHeaders); ++i) 312 rejected_headers[kRejectedHttpRequestHeaders[i]] = 313 static_cast<FlashNavigateUsage>(i); 314 } 315 316 net::HttpUtil::HeadersIterator header_iter( 317 data.headers.begin(), data.headers.end(), "\n\r"); 318 bool rejected = false; 319 while (header_iter.GetNext()) { 320 std::string lower_case_header_name = 321 base::StringToLowerASCII(header_iter.name()); 322 if (!IsSimpleHeader(lower_case_header_name, header_iter.values())) { 323 rejected = true; 324 325 std::map<std::string, FlashNavigateUsage>::const_iterator iter = 326 rejected_headers.find(lower_case_header_name); 327 FlashNavigateUsage usage = 328 iter != rejected_headers.end() ? iter->second : REJECT_OTHER_HEADERS; 329 RecordFlashNavigateUsage(usage); 330 } 331 } 332 333 RecordFlashNavigateUsage(TOTAL_NAVIGATE_REQUESTS); 334 if (rejected) { 335 RecordFlashNavigateUsage(TOTAL_REJECTED_NAVIGATE_REQUESTS); 336 return PP_ERROR_NOACCESS; 337 } 338 339 // Navigate may call into Javascript (e.g. with a "javascript:" URL), 340 // or do things like navigate away from the page, either one of which will 341 // need to re-enter into the plugin. It is safe, because it is essentially 342 // equivalent to NPN_GetURL, where Flash would expect re-entrancy. 343 ppapi::proxy::HostDispatcher* host_dispatcher = 344 ppapi::proxy::HostDispatcher::GetForInstance(pp_instance()); 345 host_dispatcher->set_allow_plugin_reentrancy(); 346 347 // Grab a weak pointer to ourselves on the stack so we can check if we are 348 // still alive. 349 base::WeakPtr<PepperFlashRendererHost> weak_ptr = weak_factory_.GetWeakPtr(); 350 // Keep track of reply contexts in case we are destroyed during a Navigate 351 // call. Even if we are destroyed, we still need to send these replies to 352 // unblock the plugin process. 353 navigate_replies_.push_back(host_context->MakeReplyMessageContext()); 354 plugin_instance->Navigate(data, target.c_str(), from_user_action); 355 // This object might have been destroyed by this point. If it is destroyed 356 // the reply will be sent in the destructor. Otherwise send the reply here. 357 if (weak_ptr.get()) { 358 SendReply(navigate_replies_.back(), IPC::Message()); 359 navigate_replies_.pop_back(); 360 } 361 362 // Return PP_OK_COMPLETIONPENDING so that no reply is automatically sent. 363 return PP_OK_COMPLETIONPENDING; 364 } 365 366 int32_t PepperFlashRendererHost::OnIsRectTopmost( 367 ppapi::host::HostMessageContext* host_context, 368 const PP_Rect& rect) { 369 content::PepperPluginInstance* plugin_instance = 370 host_->GetPluginInstance(pp_instance()); 371 if (plugin_instance && 372 plugin_instance->IsRectTopmost(gfx::Rect( 373 rect.point.x, rect.point.y, rect.size.width, rect.size.height))) 374 return PP_OK; 375 return PP_ERROR_FAILED; 376 } 377 378 int32_t PepperFlashRendererHost::OnInvokePrinting( 379 ppapi::host::HostMessageContext* host_context) { 380 pdf::PPB_PDF_Impl::InvokePrintingForInstance(pp_instance()); 381 return PP_OK; 382 } 383