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.
5 #include "chrome/renderer/pepper/pepper_flash_renderer_host.h"
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/geometry/rect.h"
38 using ppapi::thunk::EnterResourceNoLock
;
39 using ppapi::thunk::PPB_ImageData_API
;
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
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.
51 // TODO(yzshen): We should be able to remove the histogram recording code once
53 const char* const kRejectedHttpRequestHeaders
[] = {
56 "content-encoding", //
58 "content-type", // If the media type is not one of those covered by the
59 // simple header definition.
65 "if-unmodified-since", //
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,
77 REJECT_CONTENT_ENCODING
,
85 REJECT_IF_UNMODIFIED_SINCE
,
89 // The navigate request is rejected because of headers not listed above
90 // (e.g., custom headers).
93 // Total number of rejected navigate requests.
94 TOTAL_REJECTED_NAVIGATE_REQUESTS
,
96 // Total number of navigate requests.
97 TOTAL_NAVIGATE_REQUESTS
,
98 FLASH_NAVIGATE_USAGE_ENUM_COUNT
101 static base::LazyInstance
<std::map
<std::string
, FlashNavigateUsage
> >
102 g_rejected_headers
= LAZY_INSTANCE_INITIALIZER
;
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") {
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
,
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";
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
);
137 PepperFlashRendererHost::PepperFlashRendererHost(
138 content::RendererPpapiHost
* host
,
139 PP_Instance instance
,
140 PP_Resource resource
)
141 : ResourceHost(host
->GetPpapiHost(), instance
, resource
),
143 weak_factory_(this) {}
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());
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
,
159 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_Flash_SetInstanceAlwaysOnTop
,
160 OnSetInstanceAlwaysOnTop
)
161 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_Flash_DrawGlyphs
,
163 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_Flash_Navigate
, OnNavigate
)
164 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_Flash_IsRectTopmost
,
166 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_Flash_InvokePrinting
,
168 PPAPI_END_MESSAGE_MAP()
169 return PP_ERROR_FAILED
;
172 int32_t PepperFlashRendererHost::OnGetProxyForURL(
173 ppapi::host::HostMessageContext
* host_context
,
174 const std::string
& url
) {
176 if (!gurl
.is_valid())
177 return PP_ERROR_FAILED
;
179 bool result
= content::RenderThread::Get()->ResolveProxy(gurl
, &proxy
);
181 return PP_ERROR_FAILED
;
182 host_context
->reply_msg
= PpapiPluginMsg_Flash_GetProxyForURLReply(proxy
);
186 int32_t PepperFlashRendererHost::OnSetInstanceAlwaysOnTop(
187 ppapi::host::HostMessageContext
* host_context
,
189 content::PepperPluginInstance
* plugin_instance
=
190 host_
->GetPluginInstance(pp_instance());
192 plugin_instance
->SetAlwaysOnTop(on_top
);
193 // Since no reply is sent for this message, it doesn't make sense to return an
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
;
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
)));
215 return PP_ERROR_FAILED
;
217 EnterResourceNoLock
<PPB_ImageData_API
> enter(
218 params
.image_data
.host_resource(), true);
220 return PP_ERROR_FAILED
;
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;
227 needs_unmapping
= true;
229 canvas
= image
->GetPlatformCanvas();
231 return PP_ERROR_FAILED
; // Failure mapping.
234 SkAutoCanvasRestore
acr(canvas
, true);
236 // Clip is applied in pixels before the transform.
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
);
243 // Convert & set the 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
);
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);
268 SkScalar x
= SkIntToScalar(params
.position
.x
);
269 SkScalar y
= SkIntToScalar(params
.position
.y
);
271 // Build up the skia advances.
272 size_t glyph_count
= params
.glyph_indices
.size();
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
);
284 ¶ms
.glyph_indices
[0], glyph_count
* 2, sk_positions
, paint
);
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
;
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
);
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())) {
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
);
333 RecordFlashNavigateUsage(TOTAL_NAVIGATE_REQUESTS
);
335 RecordFlashNavigateUsage(TOTAL_REJECTED_NAVIGATE_REQUESTS
);
336 return PP_ERROR_NOACCESS
;
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();
347 // Grab a weak pointer to ourselves on the stack so we can check if we are
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();
362 // Return PP_OK_COMPLETIONPENDING so that no reply is automatically sent.
363 return PP_OK_COMPLETIONPENDING
;
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
)))
375 return PP_ERROR_FAILED
;
378 int32_t PepperFlashRendererHost::OnInvokePrinting(
379 ppapi::host::HostMessageContext
* host_context
) {
380 pdf::PPB_PDF_Impl::InvokePrintingForInstance(pp_instance());