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 "chrome/renderer/pepper/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 99 FLASH_NAVIGATE_USAGE_ENUM_COUNT 100 }; 101 102 static base::LazyInstance<std::map<std::string, FlashNavigateUsage> > 103 g_rejected_headers = LAZY_INSTANCE_INITIALIZER; 104 105 bool IsSimpleHeader(const std::string& lower_case_header_name, 106 const std::string& header_value) { 107 if (lower_case_header_name == "accept" || 108 lower_case_header_name == "accept-language" || 109 lower_case_header_name == "content-language") { 110 return true; 111 } 112 113 if (lower_case_header_name == "content-type") { 114 std::string lower_case_mime_type; 115 std::string lower_case_charset; 116 bool had_charset = false; 117 net::HttpUtil::ParseContentType(header_value, &lower_case_mime_type, 118 &lower_case_charset, &had_charset, NULL); 119 return lower_case_mime_type == "application/x-www-form-urlencoded" || 120 lower_case_mime_type == "multipart/form-data" || 121 lower_case_mime_type == "text/plain"; 122 } 123 124 return false; 125 } 126 127 void RecordFlashNavigateUsage(FlashNavigateUsage usage) { 128 DCHECK_NE(FLASH_NAVIGATE_USAGE_ENUM_COUNT, usage); 129 UMA_HISTOGRAM_ENUMERATION("Plugin.FlashNavigateUsage", usage, 130 FLASH_NAVIGATE_USAGE_ENUM_COUNT); 131 } 132 133 } // namespace 134 135 PepperFlashRendererHost::PepperFlashRendererHost( 136 content::RendererPpapiHost* host, 137 PP_Instance instance, 138 PP_Resource resource) 139 : ResourceHost(host->GetPpapiHost(), instance, resource), 140 host_(host), 141 weak_factory_(this) { 142 } 143 144 PepperFlashRendererHost::~PepperFlashRendererHost() { 145 // This object may be destroyed in the middle of a sync message. If that is 146 // the case, make sure we respond to all the pending navigate calls. 147 std::vector<ppapi::host::ReplyMessageContext>::reverse_iterator it; 148 for (it = navigate_replies_.rbegin(); it != navigate_replies_.rend(); ++it) 149 SendReply(*it, IPC::Message()); 150 } 151 152 int32_t PepperFlashRendererHost::OnResourceMessageReceived( 153 const IPC::Message& msg, 154 ppapi::host::HostMessageContext* context) { 155 IPC_BEGIN_MESSAGE_MAP(PepperFlashRendererHost, msg) 156 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_Flash_GetProxyForURL, 157 OnGetProxyForURL); 158 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_Flash_SetInstanceAlwaysOnTop, 159 OnSetInstanceAlwaysOnTop); 160 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_Flash_DrawGlyphs, 161 OnDrawGlyphs); 162 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_Flash_Navigate, 163 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 IPC_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( 213 SkTypeface::CreateFromName(params.font_desc.face.c_str(), 214 static_cast<SkTypeface::Style>(style))); 215 if (!typeface) 216 return PP_ERROR_FAILED; 217 218 EnterResourceNoLock<PPB_ImageData_API> enter( 219 params.image_data.host_resource(), true); 220 if (enter.failed()) 221 return PP_ERROR_FAILED; 222 223 // Set up the canvas. 224 PPB_ImageData_API* image = static_cast<PPB_ImageData_API*>( 225 enter.object()); 226 SkCanvas* canvas = image->GetPlatformCanvas(); 227 bool needs_unmapping = false; 228 if (!canvas) { 229 needs_unmapping = true; 230 image->Map(); 231 canvas = image->GetPlatformCanvas(); 232 if (!canvas) 233 return PP_ERROR_FAILED; // Failure mapping. 234 } 235 236 SkAutoCanvasRestore acr(canvas, true); 237 238 // Clip is applied in pixels before the transform. 239 SkRect clip_rect = { 240 SkIntToScalar(params.clip.point.x), 241 SkIntToScalar(params.clip.point.y), 242 SkIntToScalar(params.clip.point.x + params.clip.size.width), 243 SkIntToScalar(params.clip.point.y + params.clip.size.height) 244 }; 245 canvas->clipRect(clip_rect); 246 247 // Convert & set the matrix. 248 SkMatrix matrix; 249 matrix.set(SkMatrix::kMScaleX, SkFloatToScalar(params.transformation[0][0])); 250 matrix.set(SkMatrix::kMSkewX, SkFloatToScalar(params.transformation[0][1])); 251 matrix.set(SkMatrix::kMTransX, SkFloatToScalar(params.transformation[0][2])); 252 matrix.set(SkMatrix::kMSkewY, SkFloatToScalar(params.transformation[1][0])); 253 matrix.set(SkMatrix::kMScaleY, SkFloatToScalar(params.transformation[1][1])); 254 matrix.set(SkMatrix::kMTransY, SkFloatToScalar(params.transformation[1][2])); 255 matrix.set(SkMatrix::kMPersp0, SkFloatToScalar(params.transformation[2][0])); 256 matrix.set(SkMatrix::kMPersp1, SkFloatToScalar(params.transformation[2][1])); 257 matrix.set(SkMatrix::kMPersp2, SkFloatToScalar(params.transformation[2][2])); 258 canvas->concat(matrix); 259 260 SkPaint paint; 261 paint.setColor(params.color); 262 paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); 263 paint.setAntiAlias(true); 264 paint.setHinting(SkPaint::kFull_Hinting); 265 paint.setTextSize(SkIntToScalar(params.font_desc.size)); 266 paint.setTypeface(typeface.get()); // Takes a ref and manages lifetime. 267 if (params.allow_subpixel_aa) { 268 paint.setSubpixelText(true); 269 paint.setLCDRenderText(true); 270 } 271 272 SkScalar x = SkIntToScalar(params.position.x); 273 SkScalar y = SkIntToScalar(params.position.y); 274 275 // Build up the skia advances. 276 size_t glyph_count = params.glyph_indices.size(); 277 if (glyph_count) { 278 std::vector<SkPoint> storage; 279 storage.resize(glyph_count); 280 SkPoint* sk_positions = &storage[0]; 281 for (uint32_t i = 0; i < glyph_count; i++) { 282 sk_positions[i].set(x, y); 283 x += SkFloatToScalar(params.glyph_advances[i].x); 284 y += SkFloatToScalar(params.glyph_advances[i].y); 285 } 286 287 canvas->drawPosText(¶ms.glyph_indices[0], glyph_count * 2, sk_positions, 288 paint); 289 } 290 291 if (needs_unmapping) 292 image->Unmap(); 293 294 return PP_OK; 295 } 296 297 // CAUTION: This code is subtle because Navigate is a sync call which may 298 // cause re-entrancy or cause the instance to be destroyed. If the instance 299 // is destroyed we need to ensure that we respond to all outstanding sync 300 // messages so that the plugin process does not remain blocked. 301 int32_t PepperFlashRendererHost::OnNavigate( 302 ppapi::host::HostMessageContext* host_context, 303 const ppapi::URLRequestInfoData& data, 304 const std::string& target, 305 bool from_user_action) { 306 // If our PepperPluginInstance is already destroyed, just return a failure. 307 content::PepperPluginInstance* plugin_instance = 308 host_->GetPluginInstance(pp_instance()); 309 if (!plugin_instance) 310 return PP_ERROR_FAILED; 311 312 std::map<std::string, FlashNavigateUsage>& rejected_headers = 313 g_rejected_headers.Get(); 314 if (rejected_headers.empty()) { 315 for (size_t i = 0; i < arraysize(kRejectedHttpRequestHeaders); ++i) 316 rejected_headers[kRejectedHttpRequestHeaders[i]] = 317 static_cast<FlashNavigateUsage>(i); 318 } 319 320 net::HttpUtil::HeadersIterator header_iter(data.headers.begin(), 321 data.headers.end(), 322 "\n\r"); 323 bool rejected = false; 324 while (header_iter.GetNext()) { 325 std::string lower_case_header_name = StringToLowerASCII(header_iter.name()); 326 if (!IsSimpleHeader(lower_case_header_name, header_iter.values())) { 327 rejected = true; 328 329 std::map<std::string, FlashNavigateUsage>::const_iterator iter = 330 rejected_headers.find(lower_case_header_name); 331 FlashNavigateUsage usage = iter != rejected_headers.end() ? 332 iter->second : REJECT_OTHER_HEADERS; 333 RecordFlashNavigateUsage(usage); 334 } 335 } 336 337 RecordFlashNavigateUsage(TOTAL_NAVIGATE_REQUESTS); 338 if (rejected) { 339 RecordFlashNavigateUsage(TOTAL_REJECTED_NAVIGATE_REQUESTS); 340 return PP_ERROR_NOACCESS; 341 } 342 343 // Navigate may call into Javascript (e.g. with a "javascript:" URL), 344 // or do things like navigate away from the page, either one of which will 345 // need to re-enter into the plugin. It is safe, because it is essentially 346 // equivalent to NPN_GetURL, where Flash would expect re-entrancy. 347 ppapi::proxy::HostDispatcher* host_dispatcher = 348 ppapi::proxy::HostDispatcher::GetForInstance(pp_instance()); 349 host_dispatcher->set_allow_plugin_reentrancy(); 350 351 // Grab a weak pointer to ourselves on the stack so we can check if we are 352 // still alive. 353 base::WeakPtr<PepperFlashRendererHost> weak_ptr = weak_factory_.GetWeakPtr(); 354 // Keep track of reply contexts in case we are destroyed during a Navigate 355 // call. Even if we are destroyed, we still need to send these replies to 356 // unblock the plugin process. 357 navigate_replies_.push_back(host_context->MakeReplyMessageContext()); 358 plugin_instance->Navigate(data, target.c_str(), from_user_action); 359 // This object might have been destroyed by this point. If it is destroyed 360 // the reply will be sent in the destructor. Otherwise send the reply here. 361 if (weak_ptr.get()) { 362 SendReply(navigate_replies_.back(), IPC::Message()); 363 navigate_replies_.pop_back(); 364 } 365 366 // Return PP_OK_COMPLETIONPENDING so that no reply is automatically sent. 367 return PP_OK_COMPLETIONPENDING; 368 } 369 370 int32_t PepperFlashRendererHost::OnIsRectTopmost( 371 ppapi::host::HostMessageContext* host_context, 372 const PP_Rect& rect) { 373 content::PepperPluginInstance* plugin_instance = 374 host_->GetPluginInstance(pp_instance()); 375 if (plugin_instance && plugin_instance->IsRectTopmost( 376 gfx::Rect(rect.point.x, rect.point.y,rect.size.width, rect.size.height))) 377 return PP_OK; 378 return PP_ERROR_FAILED; 379 } 380 381 int32_t PepperFlashRendererHost::OnInvokePrinting( 382 ppapi::host::HostMessageContext* host_context) { 383 PPB_PDF_Impl::InvokePrintingForInstance(pp_instance()); 384 return PP_OK; 385 } 386