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/pepper_pdf_host.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/SkTypeface.h"
34 #include "ui/gfx/geometry/rect.h"
37 using ppapi::thunk::EnterResourceNoLock
;
38 using ppapi::thunk::PPB_ImageData_API
;
42 // Some non-simple HTTP request headers that Flash may set.
43 // (Please see http://www.w3.org/TR/cors/#simple-header for the definition of
46 // The list and the enum defined below are used to collect data about request
47 // headers used in PPB_Flash.Navigate() calls, in order to understand the impact
48 // of rejecting PPB_Flash.Navigate() requests with non-simple headers.
50 // TODO(yzshen): We should be able to remove the histogram recording code once
52 const char* const kRejectedHttpRequestHeaders
[] = {
55 "content-encoding", //
57 "content-type", // If the media type is not one of those covered by the
58 // simple header definition.
64 "if-unmodified-since", //
69 // Please note that new entries should be added right above
70 // FLASH_NAVIGATE_USAGE_ENUM_COUNT, and existing entries shouldn't be re-ordered
71 // or removed, since this ordering is used in a histogram.
72 enum FlashNavigateUsage
{
73 // This section must be in the same order as kRejectedHttpRequestHeaders.
74 REJECT_AUTHORIZATION
= 0,
76 REJECT_CONTENT_ENCODING
,
84 REJECT_IF_UNMODIFIED_SINCE
,
88 // The navigate request is rejected because of headers not listed above
89 // (e.g., custom headers).
92 // Total number of rejected navigate requests.
93 TOTAL_REJECTED_NAVIGATE_REQUESTS
,
95 // Total number of navigate requests.
96 TOTAL_NAVIGATE_REQUESTS
,
97 FLASH_NAVIGATE_USAGE_ENUM_COUNT
100 static base::LazyInstance
<std::map
<std::string
, FlashNavigateUsage
> >
101 g_rejected_headers
= LAZY_INSTANCE_INITIALIZER
;
103 bool IsSimpleHeader(const std::string
& lower_case_header_name
,
104 const std::string
& header_value
) {
105 if (lower_case_header_name
== "accept" ||
106 lower_case_header_name
== "accept-language" ||
107 lower_case_header_name
== "content-language") {
111 if (lower_case_header_name
== "content-type") {
112 std::string lower_case_mime_type
;
113 std::string lower_case_charset
;
114 bool had_charset
= false;
115 net::HttpUtil::ParseContentType(header_value
,
116 &lower_case_mime_type
,
120 return lower_case_mime_type
== "application/x-www-form-urlencoded" ||
121 lower_case_mime_type
== "multipart/form-data" ||
122 lower_case_mime_type
== "text/plain";
128 void RecordFlashNavigateUsage(FlashNavigateUsage usage
) {
129 DCHECK_NE(FLASH_NAVIGATE_USAGE_ENUM_COUNT
, usage
);
130 UMA_HISTOGRAM_ENUMERATION(
131 "Plugin.FlashNavigateUsage", usage
, FLASH_NAVIGATE_USAGE_ENUM_COUNT
);
136 PepperFlashRendererHost::PepperFlashRendererHost(
137 content::RendererPpapiHost
* host
,
138 PP_Instance instance
,
139 PP_Resource resource
)
140 : ResourceHost(host
->GetPpapiHost(), instance
, resource
),
142 weak_factory_(this) {}
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());
152 int32_t PepperFlashRendererHost::OnResourceMessageReceived(
153 const IPC::Message
& msg
,
154 ppapi::host::HostMessageContext
* context
) {
155 PPAPI_BEGIN_MESSAGE_MAP(PepperFlashRendererHost
, msg
)
156 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_Flash_GetProxyForURL
,
158 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_Flash_SetInstanceAlwaysOnTop
,
159 OnSetInstanceAlwaysOnTop
)
160 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_Flash_DrawGlyphs
,
162 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_Flash_Navigate
, OnNavigate
)
163 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_Flash_IsRectTopmost
,
165 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_Flash_InvokePrinting
,
167 PPAPI_END_MESSAGE_MAP()
168 return PP_ERROR_FAILED
;
171 int32_t PepperFlashRendererHost::OnGetProxyForURL(
172 ppapi::host::HostMessageContext
* host_context
,
173 const std::string
& url
) {
175 if (!gurl
.is_valid())
176 return PP_ERROR_FAILED
;
178 bool result
= content::RenderThread::Get()->ResolveProxy(gurl
, &proxy
);
180 return PP_ERROR_FAILED
;
181 host_context
->reply_msg
= PpapiPluginMsg_Flash_GetProxyForURLReply(proxy
);
185 int32_t PepperFlashRendererHost::OnSetInstanceAlwaysOnTop(
186 ppapi::host::HostMessageContext
* host_context
,
188 content::PepperPluginInstance
* plugin_instance
=
189 host_
->GetPluginInstance(pp_instance());
191 plugin_instance
->SetAlwaysOnTop(on_top
);
192 // Since no reply is sent for this message, it doesn't make sense to return an
197 int32_t PepperFlashRendererHost::OnDrawGlyphs(
198 ppapi::host::HostMessageContext
* host_context
,
199 ppapi::proxy::PPBFlash_DrawGlyphs_Params params
) {
200 if (params
.glyph_indices
.size() != params
.glyph_advances
.size() ||
201 params
.glyph_indices
.empty())
202 return PP_ERROR_FAILED
;
204 // Set up the typeface.
205 int style
= SkTypeface::kNormal
;
206 if (static_cast<PP_BrowserFont_Trusted_Weight
>(params
.font_desc
.weight
) >=
207 PP_BROWSERFONT_TRUSTED_WEIGHT_BOLD
)
208 style
|= SkTypeface::kBold
;
209 if (params
.font_desc
.italic
)
210 style
|= SkTypeface::kItalic
;
211 skia::RefPtr
<SkTypeface
> typeface
= skia::AdoptRef(SkTypeface::CreateFromName(
212 params
.font_desc
.face
.c_str(), static_cast<SkTypeface::Style
>(style
)));
214 return PP_ERROR_FAILED
;
216 EnterResourceNoLock
<PPB_ImageData_API
> enter(
217 params
.image_data
.host_resource(), true);
219 return PP_ERROR_FAILED
;
221 // Set up the canvas.
222 PPB_ImageData_API
* image
= static_cast<PPB_ImageData_API
*>(enter
.object());
223 SkCanvas
* canvas
= image
->GetCanvas();
224 bool needs_unmapping
= false;
226 needs_unmapping
= true;
228 canvas
= image
->GetCanvas();
230 return PP_ERROR_FAILED
; // Failure mapping.
233 SkAutoCanvasRestore
acr(canvas
, true);
235 // Clip is applied in pixels before the transform.
237 SkIntToScalar(params
.clip
.point
.x
), SkIntToScalar(params
.clip
.point
.y
),
238 SkIntToScalar(params
.clip
.point
.x
+ params
.clip
.size
.width
),
239 SkIntToScalar(params
.clip
.point
.y
+ params
.clip
.size
.height
)};
240 canvas
->clipRect(clip_rect
);
242 // Convert & set the matrix.
244 matrix
.set(SkMatrix::kMScaleX
, SkFloatToScalar(params
.transformation
[0][0]));
245 matrix
.set(SkMatrix::kMSkewX
, SkFloatToScalar(params
.transformation
[0][1]));
246 matrix
.set(SkMatrix::kMTransX
, SkFloatToScalar(params
.transformation
[0][2]));
247 matrix
.set(SkMatrix::kMSkewY
, SkFloatToScalar(params
.transformation
[1][0]));
248 matrix
.set(SkMatrix::kMScaleY
, SkFloatToScalar(params
.transformation
[1][1]));
249 matrix
.set(SkMatrix::kMTransY
, SkFloatToScalar(params
.transformation
[1][2]));
250 matrix
.set(SkMatrix::kMPersp0
, SkFloatToScalar(params
.transformation
[2][0]));
251 matrix
.set(SkMatrix::kMPersp1
, SkFloatToScalar(params
.transformation
[2][1]));
252 matrix
.set(SkMatrix::kMPersp2
, SkFloatToScalar(params
.transformation
[2][2]));
253 canvas
->concat(matrix
);
256 paint
.setColor(params
.color
);
257 paint
.setTextEncoding(SkPaint::kGlyphID_TextEncoding
);
258 paint
.setAntiAlias(true);
259 paint
.setHinting(SkPaint::kFull_Hinting
);
260 paint
.setTextSize(SkIntToScalar(params
.font_desc
.size
));
261 paint
.setTypeface(typeface
.get()); // Takes a ref and manages lifetime.
262 if (params
.allow_subpixel_aa
) {
263 paint
.setSubpixelText(true);
264 paint
.setLCDRenderText(true);
267 SkScalar x
= SkIntToScalar(params
.position
.x
);
268 SkScalar y
= SkIntToScalar(params
.position
.y
);
270 // Build up the skia advances.
271 size_t glyph_count
= params
.glyph_indices
.size();
273 std::vector
<SkPoint
> sk_positions(glyph_count
);
274 for (uint32_t i
= 0; i
< glyph_count
; i
++) {
275 sk_positions
[i
].set(x
, y
);
276 x
+= SkFloatToScalar(params
.glyph_advances
[i
].x
);
277 y
+= SkFloatToScalar(params
.glyph_advances
[i
].y
);
281 ¶ms
.glyph_indices
[0], glyph_count
* 2, &sk_positions
[0], paint
);
290 // CAUTION: This code is subtle because Navigate is a sync call which may
291 // cause re-entrancy or cause the instance to be destroyed. If the instance
292 // is destroyed we need to ensure that we respond to all outstanding sync
293 // messages so that the plugin process does not remain blocked.
294 int32_t PepperFlashRendererHost::OnNavigate(
295 ppapi::host::HostMessageContext
* host_context
,
296 const ppapi::URLRequestInfoData
& data
,
297 const std::string
& target
,
298 bool from_user_action
) {
299 // If our PepperPluginInstance is already destroyed, just return a failure.
300 content::PepperPluginInstance
* plugin_instance
=
301 host_
->GetPluginInstance(pp_instance());
302 if (!plugin_instance
)
303 return PP_ERROR_FAILED
;
305 std::map
<std::string
, FlashNavigateUsage
>& rejected_headers
=
306 g_rejected_headers
.Get();
307 if (rejected_headers
.empty()) {
308 for (size_t i
= 0; i
< arraysize(kRejectedHttpRequestHeaders
); ++i
)
309 rejected_headers
[kRejectedHttpRequestHeaders
[i
]] =
310 static_cast<FlashNavigateUsage
>(i
);
313 net::HttpUtil::HeadersIterator
header_iter(
314 data
.headers
.begin(), data
.headers
.end(), "\n\r");
315 bool rejected
= false;
316 while (header_iter
.GetNext()) {
317 std::string lower_case_header_name
=
318 base::ToLowerASCII(header_iter
.name());
319 if (!IsSimpleHeader(lower_case_header_name
, header_iter
.values())) {
322 std::map
<std::string
, FlashNavigateUsage
>::const_iterator iter
=
323 rejected_headers
.find(lower_case_header_name
);
324 FlashNavigateUsage usage
=
325 iter
!= rejected_headers
.end() ? iter
->second
: REJECT_OTHER_HEADERS
;
326 RecordFlashNavigateUsage(usage
);
330 RecordFlashNavigateUsage(TOTAL_NAVIGATE_REQUESTS
);
332 RecordFlashNavigateUsage(TOTAL_REJECTED_NAVIGATE_REQUESTS
);
333 return PP_ERROR_NOACCESS
;
336 // Navigate may call into Javascript (e.g. with a "javascript:" URL),
337 // or do things like navigate away from the page, either one of which will
338 // need to re-enter into the plugin. It is safe, because it is essentially
339 // equivalent to NPN_GetURL, where Flash would expect re-entrancy.
340 ppapi::proxy::HostDispatcher
* host_dispatcher
=
341 ppapi::proxy::HostDispatcher::GetForInstance(pp_instance());
342 host_dispatcher
->set_allow_plugin_reentrancy();
344 // Grab a weak pointer to ourselves on the stack so we can check if we are
346 base::WeakPtr
<PepperFlashRendererHost
> weak_ptr
= weak_factory_
.GetWeakPtr();
347 // Keep track of reply contexts in case we are destroyed during a Navigate
348 // call. Even if we are destroyed, we still need to send these replies to
349 // unblock the plugin process.
350 navigate_replies_
.push_back(host_context
->MakeReplyMessageContext());
351 plugin_instance
->Navigate(data
, target
.c_str(), from_user_action
);
352 // This object might have been destroyed by this point. If it is destroyed
353 // the reply will be sent in the destructor. Otherwise send the reply here.
354 if (weak_ptr
.get()) {
355 SendReply(navigate_replies_
.back(), IPC::Message());
356 navigate_replies_
.pop_back();
359 // Return PP_OK_COMPLETIONPENDING so that no reply is automatically sent.
360 return PP_OK_COMPLETIONPENDING
;
363 int32_t PepperFlashRendererHost::OnIsRectTopmost(
364 ppapi::host::HostMessageContext
* host_context
,
365 const PP_Rect
& rect
) {
366 content::PepperPluginInstance
* plugin_instance
=
367 host_
->GetPluginInstance(pp_instance());
368 if (plugin_instance
&&
369 plugin_instance
->IsRectTopmost(gfx::Rect(
370 rect
.point
.x
, rect
.point
.y
, rect
.size
.width
, rect
.size
.height
)))
372 return PP_ERROR_FAILED
;
375 int32_t PepperFlashRendererHost::OnInvokePrinting(
376 ppapi::host::HostMessageContext
* host_context
) {
377 pdf::PepperPDFHost::InvokePrintingForInstance(pp_instance());