Adding CreateFromData to NativeMetafileFactory
[chromium-blink-merge.git] / webkit / plugins / ppapi / ppapi_plugin_instance.cc
bloba66528581508d6e30f9f33cfaeee7f28d38144d3
1 // Copyright (c) 2011 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 "webkit/plugins/ppapi/ppapi_plugin_instance.h"
7 #include "base/logging.h"
8 #include "base/message_loop.h"
9 #include "base/metrics/histogram.h"
10 #include "base/scoped_ptr.h"
11 #include "base/utf_string_conversions.h"
12 #include "ppapi/c/dev/ppb_find_dev.h"
13 #include "ppapi/c/dev/ppb_fullscreen_dev.h"
14 #include "ppapi/c/dev/ppb_messaging_dev.h"
15 #include "ppapi/c/dev/ppb_zoom_dev.h"
16 #include "ppapi/c/dev/ppp_find_dev.h"
17 #include "ppapi/c/dev/ppp_messaging_dev.h"
18 #include "ppapi/c/dev/ppp_selection_dev.h"
19 #include "ppapi/c/dev/ppp_zoom_dev.h"
20 #include "ppapi/c/pp_input_event.h"
21 #include "ppapi/c/pp_instance.h"
22 #include "ppapi/c/pp_rect.h"
23 #include "ppapi/c/pp_resource.h"
24 #include "ppapi/c/pp_var.h"
25 #include "ppapi/c/ppb_core.h"
26 #include "ppapi/c/ppb_instance.h"
27 #include "ppapi/c/ppp_instance.h"
28 #include "printing/units.h"
29 #include "skia/ext/vector_platform_device.h"
30 #include "skia/ext/platform_canvas.h"
31 #include "third_party/WebKit/Source/WebKit/chromium/public/WebBindings.h"
32 #include "third_party/WebKit/Source/WebKit/chromium/public/WebCursorInfo.h"
33 #include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h"
34 #include "third_party/WebKit/Source/WebKit/chromium/public/WebElement.h"
35 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h"
36 #include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h"
37 #include "third_party/WebKit/Source/WebKit/chromium/public/WebPluginContainer.h"
38 #include "third_party/WebKit/Source/WebKit/chromium/public/WebRect.h"
39 #include "third_party/WebKit/Source/WebKit/chromium/public/WebString.h"
40 #include "third_party/WebKit/Source/WebKit/chromium/public/WebURLRequest.h"
41 #include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h"
42 #include "ui/gfx/rect.h"
43 #include "ui/gfx/skia_util.h"
44 #include "webkit/plugins/ppapi/common.h"
45 #include "webkit/plugins/ppapi/event_conversion.h"
46 #include "webkit/plugins/ppapi/fullscreen_container.h"
47 #include "webkit/plugins/ppapi/message_channel.h"
48 #include "webkit/plugins/ppapi/plugin_delegate.h"
49 #include "webkit/plugins/ppapi/plugin_module.h"
50 #include "webkit/plugins/ppapi/plugin_object.h"
51 #include "webkit/plugins/ppapi/ppb_buffer_impl.h"
52 #include "webkit/plugins/ppapi/ppb_graphics_2d_impl.h"
53 #include "webkit/plugins/ppapi/ppb_image_data_impl.h"
54 #include "webkit/plugins/ppapi/ppb_surface_3d_impl.h"
55 #include "webkit/plugins/ppapi/ppb_url_loader_impl.h"
56 #include "webkit/plugins/ppapi/ppp_pdf.h"
57 #include "webkit/plugins/ppapi/string.h"
58 #include "webkit/plugins/ppapi/var.h"
59 #include "webkit/plugins/sad_plugin.h"
61 #if defined(OS_POSIX)
62 #include "printing/native_metafile.h"
63 #endif
65 #if defined(OS_MACOSX)
66 #include "base/mac/mac_util.h"
67 #include "base/mac/scoped_cftyperef.h"
68 #include "printing/native_metafile_factory.h"
69 #endif
71 #if defined(OS_LINUX)
72 #include "printing/native_metafile_skia_wrapper.h"
73 #endif
75 #if defined(OS_WIN)
76 #include "ui/gfx/codec/jpeg_codec.h"
77 #include "ui/gfx/gdi_util.h"
78 #endif
80 using WebKit::WebBindings;
81 using WebKit::WebCanvas;
82 using WebKit::WebCursorInfo;
83 using WebKit::WebDocument;
84 using WebKit::WebFrame;
85 using WebKit::WebInputEvent;
86 using WebKit::WebPluginContainer;
87 using WebKit::WebString;
88 using WebKit::WebURLRequest;
89 using WebKit::WebView;
91 namespace webkit {
92 namespace ppapi {
94 #if defined(OS_WIN)
95 // Exported by pdf.dll
96 typedef bool (*RenderPDFPageToDCProc)(
97 const unsigned char* pdf_buffer, int buffer_size, int page_number, HDC dc,
98 int dpi_x, int dpi_y, int bounds_origin_x, int bounds_origin_y,
99 int bounds_width, int bounds_height, bool fit_to_bounds,
100 bool stretch_to_bounds, bool keep_aspect_ratio, bool center_in_bounds);
101 #endif // defined(OS_WIN)
103 namespace {
105 #define COMPILE_ASSERT_MATCHING_ENUM(webkit_name, np_name) \
106 COMPILE_ASSERT(static_cast<int>(WebCursorInfo::webkit_name) \
107 == static_cast<int>(np_name), \
108 mismatching_enums)
110 COMPILE_ASSERT_MATCHING_ENUM(TypePointer, PP_CURSORTYPE_POINTER);
111 COMPILE_ASSERT_MATCHING_ENUM(TypeCross, PP_CURSORTYPE_CROSS);
112 COMPILE_ASSERT_MATCHING_ENUM(TypeHand, PP_CURSORTYPE_HAND);
113 COMPILE_ASSERT_MATCHING_ENUM(TypeIBeam, PP_CURSORTYPE_IBEAM);
114 COMPILE_ASSERT_MATCHING_ENUM(TypeWait, PP_CURSORTYPE_WAIT);
115 COMPILE_ASSERT_MATCHING_ENUM(TypeHelp, PP_CURSORTYPE_HELP);
116 COMPILE_ASSERT_MATCHING_ENUM(TypeEastResize, PP_CURSORTYPE_EASTRESIZE);
117 COMPILE_ASSERT_MATCHING_ENUM(TypeNorthResize, PP_CURSORTYPE_NORTHRESIZE);
118 COMPILE_ASSERT_MATCHING_ENUM(TypeNorthEastResize,
119 PP_CURSORTYPE_NORTHEASTRESIZE);
120 COMPILE_ASSERT_MATCHING_ENUM(TypeNorthWestResize,
121 PP_CURSORTYPE_NORTHWESTRESIZE);
122 COMPILE_ASSERT_MATCHING_ENUM(TypeSouthResize, PP_CURSORTYPE_SOUTHRESIZE);
123 COMPILE_ASSERT_MATCHING_ENUM(TypeSouthEastResize,
124 PP_CURSORTYPE_SOUTHEASTRESIZE);
125 COMPILE_ASSERT_MATCHING_ENUM(TypeSouthWestResize,
126 PP_CURSORTYPE_SOUTHWESTRESIZE);
127 COMPILE_ASSERT_MATCHING_ENUM(TypeWestResize, PP_CURSORTYPE_WESTRESIZE);
128 COMPILE_ASSERT_MATCHING_ENUM(TypeNorthSouthResize,
129 PP_CURSORTYPE_NORTHSOUTHRESIZE);
130 COMPILE_ASSERT_MATCHING_ENUM(TypeEastWestResize, PP_CURSORTYPE_EASTWESTRESIZE);
131 COMPILE_ASSERT_MATCHING_ENUM(TypeNorthEastSouthWestResize,
132 PP_CURSORTYPE_NORTHEASTSOUTHWESTRESIZE);
133 COMPILE_ASSERT_MATCHING_ENUM(TypeNorthWestSouthEastResize,
134 PP_CURSORTYPE_NORTHWESTSOUTHEASTRESIZE);
135 COMPILE_ASSERT_MATCHING_ENUM(TypeColumnResize, PP_CURSORTYPE_COLUMNRESIZE);
136 COMPILE_ASSERT_MATCHING_ENUM(TypeRowResize, PP_CURSORTYPE_ROWRESIZE);
137 COMPILE_ASSERT_MATCHING_ENUM(TypeMiddlePanning, PP_CURSORTYPE_MIDDLEPANNING);
138 COMPILE_ASSERT_MATCHING_ENUM(TypeEastPanning, PP_CURSORTYPE_EASTPANNING);
139 COMPILE_ASSERT_MATCHING_ENUM(TypeNorthPanning, PP_CURSORTYPE_NORTHPANNING);
140 COMPILE_ASSERT_MATCHING_ENUM(TypeNorthEastPanning,
141 PP_CURSORTYPE_NORTHEASTPANNING);
142 COMPILE_ASSERT_MATCHING_ENUM(TypeNorthWestPanning,
143 PP_CURSORTYPE_NORTHWESTPANNING);
144 COMPILE_ASSERT_MATCHING_ENUM(TypeSouthPanning, PP_CURSORTYPE_SOUTHPANNING);
145 COMPILE_ASSERT_MATCHING_ENUM(TypeSouthEastPanning,
146 PP_CURSORTYPE_SOUTHEASTPANNING);
147 COMPILE_ASSERT_MATCHING_ENUM(TypeSouthWestPanning,
148 PP_CURSORTYPE_SOUTHWESTPANNING);
149 COMPILE_ASSERT_MATCHING_ENUM(TypeWestPanning, PP_CURSORTYPE_WESTPANNING);
150 COMPILE_ASSERT_MATCHING_ENUM(TypeMove, PP_CURSORTYPE_MOVE);
151 COMPILE_ASSERT_MATCHING_ENUM(TypeVerticalText, PP_CURSORTYPE_VERTICALTEXT);
152 COMPILE_ASSERT_MATCHING_ENUM(TypeCell, PP_CURSORTYPE_CELL);
153 COMPILE_ASSERT_MATCHING_ENUM(TypeContextMenu, PP_CURSORTYPE_CONTEXTMENU);
154 COMPILE_ASSERT_MATCHING_ENUM(TypeAlias, PP_CURSORTYPE_ALIAS);
155 COMPILE_ASSERT_MATCHING_ENUM(TypeProgress, PP_CURSORTYPE_PROGRESS);
156 COMPILE_ASSERT_MATCHING_ENUM(TypeNoDrop, PP_CURSORTYPE_NODROP);
157 COMPILE_ASSERT_MATCHING_ENUM(TypeCopy, PP_CURSORTYPE_COPY);
158 COMPILE_ASSERT_MATCHING_ENUM(TypeNone, PP_CURSORTYPE_NONE);
159 COMPILE_ASSERT_MATCHING_ENUM(TypeNotAllowed, PP_CURSORTYPE_NOTALLOWED);
160 COMPILE_ASSERT_MATCHING_ENUM(TypeZoomIn, PP_CURSORTYPE_ZOOMIN);
161 COMPILE_ASSERT_MATCHING_ENUM(TypeZoomOut, PP_CURSORTYPE_ZOOMOUT);
162 COMPILE_ASSERT_MATCHING_ENUM(TypeGrab, PP_CURSORTYPE_GRAB);
163 COMPILE_ASSERT_MATCHING_ENUM(TypeGrabbing, PP_CURSORTYPE_GRABBING);
164 // Do not assert WebCursorInfo::TypeCustom == PP_CURSORTYPE_CUSTOM;
165 // PP_CURSORTYPE_CUSTOM is pinned to allow new cursor types.
167 void RectToPPRect(const gfx::Rect& input, PP_Rect* output) {
168 *output = PP_MakeRectFromXYWH(input.x(), input.y(),
169 input.width(), input.height());
172 PP_Var GetWindowObject(PP_Instance instance_id) {
173 PluginInstance* instance = ResourceTracker::Get()->GetInstance(instance_id);
174 if (!instance)
175 return PP_MakeUndefined();
176 return instance->GetWindowObject();
179 PP_Var GetOwnerElementObject(PP_Instance instance_id) {
180 PluginInstance* instance = ResourceTracker::Get()->GetInstance(instance_id);
181 if (!instance)
182 return PP_MakeUndefined();
183 return instance->GetOwnerElementObject();
186 PP_Bool BindGraphics(PP_Instance instance_id, PP_Resource graphics_id) {
187 PluginInstance* instance = ResourceTracker::Get()->GetInstance(instance_id);
188 if (!instance)
189 return PP_FALSE;
190 return BoolToPPBool(instance->BindGraphics(graphics_id));
193 PP_Bool IsFullFrame(PP_Instance instance_id) {
194 PluginInstance* instance = ResourceTracker::Get()->GetInstance(instance_id);
195 if (!instance)
196 return PP_FALSE;
197 return BoolToPPBool(instance->full_frame());
200 PP_Var ExecuteScript(PP_Instance instance_id,
201 PP_Var script,
202 PP_Var* exception) {
203 PluginInstance* instance = ResourceTracker::Get()->GetInstance(instance_id);
204 if (!instance)
205 return PP_MakeUndefined();
206 return instance->ExecuteScript(script, exception);
209 const PPB_Instance ppb_instance = {
210 &GetWindowObject,
211 &GetOwnerElementObject,
212 &BindGraphics,
213 &IsFullFrame,
214 &ExecuteScript,
217 void NumberOfFindResultsChanged(PP_Instance instance_id,
218 int32_t total,
219 PP_Bool final_result) {
220 PluginInstance* instance = ResourceTracker::Get()->GetInstance(instance_id);
221 if (!instance)
222 return;
224 DCHECK_NE(instance->find_identifier(), -1);
225 instance->delegate()->NumberOfFindResultsChanged(
226 instance->find_identifier(), total, PPBoolToBool(final_result));
229 void SelectedFindResultChanged(PP_Instance instance_id,
230 int32_t index) {
231 PluginInstance* instance = ResourceTracker::Get()->GetInstance(instance_id);
232 if (!instance)
233 return;
235 DCHECK_NE(instance->find_identifier(), -1);
236 instance->delegate()->SelectedFindResultChanged(
237 instance->find_identifier(), index);
240 const PPB_Find_Dev ppb_find = {
241 &NumberOfFindResultsChanged,
242 &SelectedFindResultChanged,
245 PP_Bool IsFullscreen(PP_Instance instance_id) {
246 PluginInstance* instance = ResourceTracker::Get()->GetInstance(instance_id);
247 if (!instance)
248 return PP_FALSE;
249 return BoolToPPBool(instance->IsFullscreen());
252 PP_Bool SetFullscreen(PP_Instance instance_id, PP_Bool fullscreen) {
253 PluginInstance* instance = ResourceTracker::Get()->GetInstance(instance_id);
254 if (!instance)
255 return PP_FALSE;
256 instance->SetFullscreen(PPBoolToBool(fullscreen), true);
257 return PP_TRUE;
260 PP_Bool GetScreenSize(PP_Instance instance_id, PP_Size* size) {
261 PluginInstance* instance = ResourceTracker::Get()->GetInstance(instance_id);
262 if (!instance || !size)
263 return PP_FALSE;
264 gfx::Size screen_size = instance->delegate()->GetScreenSize();
265 *size = PP_MakeSize(screen_size.width(), screen_size.height());
266 return PP_TRUE;
269 const PPB_Fullscreen_Dev ppb_fullscreen = {
270 &IsFullscreen,
271 &SetFullscreen,
272 &GetScreenSize
275 void PostMessage(PP_Instance instance_id, PP_Var message) {
276 PluginInstance* instance = ResourceTracker::Get()->GetInstance(instance_id);
277 if (!instance)
278 return;
279 instance->PostMessage(message);
282 const PPB_Messaging_Dev ppb_messaging = {
283 &PostMessage
286 void ZoomChanged(PP_Instance instance_id, double factor) {
287 PluginInstance* instance = ResourceTracker::Get()->GetInstance(instance_id);
288 if (!instance)
289 return;
291 // We only want to tell the page to change its zoom if the whole page is the
292 // plugin. If we're in an iframe, then don't do anything.
293 if (!instance->IsFullPagePlugin())
294 return;
296 double zoom_level = WebView::zoomFactorToZoomLevel(factor);
297 // The conversino from zoom level to factor, and back, can introduce rounding
298 // errors. i.e. WebKit originally tells us 3.0, but by the time we tell the
299 // plugin and it tells us back, the level becomes 3.000000000004. Need to
300 // round or else otherwise if the user zooms out, it will go to 3.0 instead of
301 // 2.0.
302 int rounded =
303 static_cast<int>(zoom_level + (zoom_level > 0 ? 0.001 : -0.001));
304 if (abs(rounded - zoom_level) < 0.001)
305 zoom_level = rounded;
306 instance->container()->zoomLevelChanged(zoom_level);
309 void ZoomLimitsChanged(PP_Instance instance_id,
310 double minimum_factor,
311 double maximium_factor) {
312 if (minimum_factor > maximium_factor) {
313 NOTREACHED();
314 return;
317 PluginInstance* instance = ResourceTracker::Get()->GetInstance(instance_id);
318 if (!instance)
319 return;
320 instance->delegate()->ZoomLimitsChanged(minimum_factor, maximium_factor);
323 const PPB_Zoom_Dev ppb_zoom = {
324 &ZoomChanged,
325 &ZoomLimitsChanged
328 } // namespace
330 PluginInstance::PluginInstance(PluginDelegate* delegate,
331 PluginModule* module,
332 const PPP_Instance* instance_interface)
333 : delegate_(delegate),
334 module_(module),
335 instance_interface_(instance_interface),
336 pp_instance_(0),
337 container_(NULL),
338 full_frame_(false),
339 has_webkit_focus_(false),
340 has_content_area_focus_(false),
341 find_identifier_(-1),
342 plugin_find_interface_(NULL),
343 plugin_messaging_interface_(NULL),
344 plugin_pdf_interface_(NULL),
345 plugin_selection_interface_(NULL),
346 plugin_zoom_interface_(NULL),
347 checked_for_plugin_messaging_interface_(false),
348 #if defined(OS_LINUX)
349 canvas_(NULL),
350 #endif // defined(OS_LINUX)
351 plugin_print_interface_(NULL),
352 plugin_graphics_3d_interface_(NULL),
353 always_on_top_(false),
354 fullscreen_container_(NULL),
355 fullscreen_(false),
356 message_channel_(NULL),
357 sad_plugin_(NULL) {
358 pp_instance_ = ResourceTracker::Get()->AddInstance(this);
360 memset(&current_print_settings_, 0, sizeof(current_print_settings_));
361 DCHECK(delegate);
362 module_->InstanceCreated(this);
363 delegate_->InstanceCreated(this);
364 message_channel_.reset(new MessageChannel(this));
367 PluginInstance::~PluginInstance() {
368 // Free all the plugin objects. This will automatically clear the back-
369 // pointer from the NPObject so WebKit can't call into the plugin any more.
371 // Swap out the set so we can delete from it (the objects will try to
372 // unregister themselves inside the delete call).
373 PluginObjectSet plugin_object_copy;
374 live_plugin_objects_.swap(plugin_object_copy);
375 for (PluginObjectSet::iterator i = plugin_object_copy.begin();
376 i != plugin_object_copy.end(); ++i)
377 delete *i;
379 delegate_->InstanceDeleted(this);
380 module_->InstanceDeleted(this);
382 ResourceTracker::Get()->InstanceDeleted(pp_instance_);
383 #if defined(OS_LINUX)
384 ranges_.clear();
385 #endif // defined(OS_LINUX)
388 // static
389 const PPB_Instance* PluginInstance::GetInterface() {
390 return &ppb_instance;
393 // static
394 const PPB_Find_Dev* PluginInstance::GetFindInterface() {
395 return &ppb_find;
398 // static
399 const PPB_Fullscreen_Dev* PluginInstance::GetFullscreenInterface() {
400 return &ppb_fullscreen;
403 // static
404 const PPB_Messaging_Dev* PluginInstance::GetMessagingInterface() {
405 return &ppb_messaging;
408 // static
409 const PPB_Zoom_Dev* PluginInstance::GetZoomInterface() {
410 return &ppb_zoom;
413 // NOTE: Any of these methods that calls into the plugin needs to take into
414 // account that the plugin may use Var to remove the <embed> from the DOM, which
415 // will make the WebPluginImpl drop its reference, usually the last one. If a
416 // method needs to access a member of the instance after the call has returned,
417 // then it needs to keep its own reference on the stack.
419 void PluginInstance::Paint(WebCanvas* canvas,
420 const gfx::Rect& plugin_rect,
421 const gfx::Rect& paint_rect) {
422 if (module()->is_crashed()) {
423 // Crashed plugin painting.
424 if (!sad_plugin_) // Lazily initialize bitmap.
425 sad_plugin_ = delegate_->GetSadPluginBitmap();
426 if (sad_plugin_)
427 webkit::PaintSadPlugin(canvas, plugin_rect, *sad_plugin_);
428 return;
431 if (bound_graphics_2d())
432 bound_graphics_2d()->Paint(canvas, plugin_rect, paint_rect);
435 void PluginInstance::InvalidateRect(const gfx::Rect& rect) {
436 if (fullscreen_container_) {
437 if (rect.IsEmpty())
438 fullscreen_container_->Invalidate();
439 else
440 fullscreen_container_->InvalidateRect(rect);
441 } else {
442 if (!container_ || position_.IsEmpty())
443 return; // Nothing to do.
444 if (rect.IsEmpty())
445 container_->invalidate();
446 else
447 container_->invalidateRect(rect);
451 void PluginInstance::ScrollRect(int dx, int dy, const gfx::Rect& rect) {
452 if (fullscreen_container_) {
453 fullscreen_container_->ScrollRect(dx, dy, rect);
454 } else {
455 if (full_frame_) {
456 container_->scrollRect(dx, dy, rect);
457 } else {
458 // Can't do optimized scrolling since there could be other elements on top
459 // of us.
460 InvalidateRect(rect);
465 unsigned PluginInstance::GetBackingTextureId() {
466 if (!bound_graphics_3d())
467 return 0;
469 return bound_graphics_3d()->GetBackingTextureId();
472 void PluginInstance::CommitBackingTexture() {
473 if (fullscreen_container_)
474 fullscreen_container_->Invalidate();
475 else
476 container_->commitBackingTexture();
479 void PluginInstance::InstanceCrashed() {
480 // Force free all resources and vars.
481 ResourceTracker::Get()->InstanceCrashed(pp_instance());
483 // Free any associated graphics.
484 SetFullscreen(false, false);
485 bound_graphics_ = NULL;
486 InvalidateRect(gfx::Rect());
488 delegate()->PluginCrashed(this);
491 PP_Var PluginInstance::GetWindowObject() {
492 if (!container_)
493 return PP_MakeUndefined();
495 WebFrame* frame = container_->element().document().frame();
496 if (!frame)
497 return PP_MakeUndefined();
499 return ObjectVar::NPObjectToPPVar(this, frame->windowObject());
502 PP_Var PluginInstance::GetOwnerElementObject() {
503 if (!container_)
504 return PP_MakeUndefined();
505 return ObjectVar::NPObjectToPPVar(this,
506 container_->scriptableObjectForElement());
509 bool PluginInstance::BindGraphics(PP_Resource graphics_id) {
510 if (!graphics_id) {
511 // Special-case clearing the current device.
512 if (bound_graphics_.get()) {
513 if (bound_graphics_2d()) {
514 bound_graphics_2d()->BindToInstance(NULL);
515 } else if (bound_graphics_.get()) {
516 bound_graphics_3d()->BindToInstance(false);
518 setBackingTextureId(0);
519 InvalidateRect(gfx::Rect());
521 bound_graphics_ = NULL;
522 return true;
525 scoped_refptr<PPB_Graphics2D_Impl> graphics_2d =
526 Resource::GetAs<PPB_Graphics2D_Impl>(graphics_id);
527 scoped_refptr<PPB_Surface3D_Impl> graphics_3d =
528 Resource::GetAs<PPB_Surface3D_Impl>(graphics_id);
530 if (graphics_2d) {
531 // Refuse to bind if we're transitioning to fullscreen.
532 if (fullscreen_container_ && !fullscreen_)
533 return false;
534 if (!graphics_2d->BindToInstance(this))
535 return false; // Can't bind to more than one instance.
537 // See http://crbug.com/49403: this can be further optimized by keeping the
538 // old device around and painting from it.
539 if (bound_graphics_2d()) {
540 // Start the new image with the content of the old image until the plugin
541 // repaints.
542 // Use ImageDataAutoMapper to ensure the image data is valid.
543 ImageDataAutoMapper mapper(bound_graphics_2d()->image_data());
544 if (!mapper.is_valid())
545 return false;
546 const SkBitmap* old_backing_bitmap =
547 bound_graphics_2d()->image_data()->GetMappedBitmap();
548 SkRect old_size = SkRect::MakeWH(
549 SkScalar(static_cast<float>(old_backing_bitmap->width())),
550 SkScalar(static_cast<float>(old_backing_bitmap->height())));
552 SkCanvas canvas(*graphics_2d->image_data()->GetMappedBitmap());
553 canvas.drawBitmap(*old_backing_bitmap, 0, 0);
555 // Fill in any extra space with white.
556 canvas.clipRect(old_size, SkRegion::kDifference_Op);
557 canvas.drawARGB(255, 255, 255, 255);
560 bound_graphics_ = graphics_2d;
561 setBackingTextureId(0);
562 // BindToInstance will have invalidated the plugin if necessary.
563 } else if (graphics_3d) {
564 // Refuse to bind if we're transitioning to fullscreen.
565 if (fullscreen_container_ && !fullscreen_)
566 return false;
567 // Make sure graphics can only be bound to the instance it is
568 // associated with.
569 if (graphics_3d->instance() != this)
570 return false;
571 if (!graphics_3d->BindToInstance(true))
572 return false;
574 setBackingTextureId(graphics_3d->GetBackingTextureId());
575 bound_graphics_ = graphics_3d;
578 return true;
581 bool PluginInstance::SetCursor(PP_CursorType_Dev type,
582 PP_Resource custom_image,
583 const PP_Point* hot_spot) {
584 if (type != PP_CURSORTYPE_CUSTOM) {
585 cursor_.reset(new WebCursorInfo(static_cast<WebCursorInfo::Type>(type)));
586 return true;
589 if (!hot_spot)
590 return false;
592 scoped_refptr<PPB_ImageData_Impl> image_data(
593 Resource::GetAs<PPB_ImageData_Impl>(custom_image));
594 if (!image_data.get())
595 return false;
597 if (image_data->format() != PPB_ImageData_Impl::GetNativeImageDataFormat()) {
598 // TODO(yzshen): Handle the case that the image format is different from the
599 // native format.
600 NOTIMPLEMENTED();
601 return false;
604 ImageDataAutoMapper auto_mapper(image_data);
605 if (!auto_mapper.is_valid())
606 return false;
608 scoped_ptr<WebCursorInfo> custom_cursor(
609 new WebCursorInfo(WebCursorInfo::TypeCustom));
610 custom_cursor->hotSpot.x = hot_spot->x;
611 custom_cursor->hotSpot.y = hot_spot->y;
613 #if WEBKIT_USING_SKIA
614 const SkBitmap* bitmap = image_data->GetMappedBitmap();
615 // Make a deep copy, so that the cursor remains valid even after the original
616 // image data gets freed.
617 if (!bitmap->copyTo(&custom_cursor->customImage.getSkBitmap(),
618 bitmap->config())) {
619 return false;
621 #elif WEBKIT_USING_CG
622 // TODO(yzshen): Implement it.
623 NOTIMPLEMENTED();
624 return false;
625 #endif
627 cursor_.reset(custom_cursor.release());
628 return true;
631 PP_Var PluginInstance::ExecuteScript(PP_Var script, PP_Var* exception) {
632 TryCatch try_catch(module(), exception);
633 if (try_catch.has_exception())
634 return PP_MakeUndefined();
636 // Convert the script into an inconvenient NPString object.
637 scoped_refptr<StringVar> script_string(StringVar::FromPPVar(script));
638 if (!script_string) {
639 try_catch.SetException("Script param to ExecuteScript must be a string.");
640 return PP_MakeUndefined();
642 NPString np_script;
643 np_script.UTF8Characters = script_string->value().c_str();
644 np_script.UTF8Length = script_string->value().length();
646 // Get the current frame to pass to the evaluate function.
647 WebFrame* frame = container_->element().document().frame();
648 if (!frame) {
649 try_catch.SetException("No frame to execute script in.");
650 return PP_MakeUndefined();
653 NPVariant result;
654 bool ok = WebBindings::evaluate(NULL, frame->windowObject(), &np_script,
655 &result);
656 if (!ok) {
657 // TODO(brettw) bug 54011: The TryCatch isn't working properly and
658 // doesn't actually catch this exception.
659 try_catch.SetException("Exception caught");
660 WebBindings::releaseVariantValue(&result);
661 return PP_MakeUndefined();
664 PP_Var ret = Var::NPVariantToPPVar(this, &result);
665 WebBindings::releaseVariantValue(&result);
666 return ret;
669 void PluginInstance::PostMessage(PP_Var message) {
670 message_channel_->PostMessageToJavaScript(message);
673 void PluginInstance::Delete() {
674 // Keep a reference on the stack. See NOTE above.
675 scoped_refptr<PluginInstance> ref(this);
676 instance_interface_->DidDestroy(pp_instance());
678 if (fullscreen_container_) {
679 fullscreen_container_->Destroy();
680 fullscreen_container_ = NULL;
682 container_ = NULL;
685 bool PluginInstance::Initialize(WebPluginContainer* container,
686 const std::vector<std::string>& arg_names,
687 const std::vector<std::string>& arg_values,
688 bool full_frame) {
689 container_ = container;
690 full_frame_ = full_frame;
692 size_t argc = 0;
693 scoped_array<const char*> argn(new const char*[arg_names.size()]);
694 scoped_array<const char*> argv(new const char*[arg_names.size()]);
695 for (size_t i = 0; i < arg_names.size(); ++i) {
696 argn[argc] = arg_names[i].c_str();
697 argv[argc] = arg_values[i].c_str();
698 argc++;
701 return PPBoolToBool(instance_interface_->DidCreate(pp_instance(),
702 argc,
703 argn.get(),
704 argv.get()));
707 bool PluginInstance::HandleDocumentLoad(PPB_URLLoader_Impl* loader) {
708 Resource::ScopedResourceId resource(loader);
709 return PPBoolToBool(instance_interface_->HandleDocumentLoad(pp_instance(),
710 resource.id));
713 bool PluginInstance::HandleInputEvent(const WebKit::WebInputEvent& event,
714 WebCursorInfo* cursor_info) {
715 // Keep a reference on the stack. See NOTE above.
716 scoped_refptr<PluginInstance> ref(this);
717 std::vector<PP_InputEvent> pp_events;
718 CreatePPEvent(event, &pp_events);
720 // Each input event may generate more than one PP_InputEvent.
721 bool rv = false;
722 for (size_t i = 0; i < pp_events.size(); i++) {
723 rv |= PPBoolToBool(instance_interface_->HandleInputEvent(pp_instance(),
724 &pp_events[i]));
727 if (cursor_.get())
728 *cursor_info = *cursor_;
729 return rv;
732 void PluginInstance::HandleMessage(PP_Var message) {
733 // Keep a reference on the stack. See NOTE above.
734 scoped_refptr<PluginInstance> ref(this);
735 if (!LoadMessagingInterface())
736 return;
737 plugin_messaging_interface_->HandleMessage(pp_instance(), message);
740 PP_Var PluginInstance::GetInstanceObject() {
741 return instance_interface_->GetInstanceObject(pp_instance());
744 void PluginInstance::ViewChanged(const gfx::Rect& position,
745 const gfx::Rect& clip) {
746 fullscreen_ = (fullscreen_container_ != NULL);
747 position_ = position;
749 if (clip.IsEmpty()) {
750 // WebKit can give weird (x,y) positions for empty clip rects (since the
751 // position technically doesn't matter). But we want to make these
752 // consistent since this is given to the plugin, so force everything to 0
753 // in the "everything is clipped" case.
754 clip_ = gfx::Rect();
755 } else {
756 clip_ = clip;
759 PP_Rect pp_position, pp_clip;
760 RectToPPRect(position_, &pp_position);
761 RectToPPRect(clip_, &pp_clip);
762 instance_interface_->DidChangeView(pp_instance(), &pp_position, &pp_clip);
765 void PluginInstance::SetWebKitFocus(bool has_focus) {
766 if (has_webkit_focus_ == has_focus)
767 return;
769 bool old_plugin_focus = PluginHasFocus();
770 has_webkit_focus_ = has_focus;
771 if (PluginHasFocus() != old_plugin_focus) {
772 instance_interface_->DidChangeFocus(pp_instance(),
773 BoolToPPBool(PluginHasFocus()));
777 void PluginInstance::SetContentAreaFocus(bool has_focus) {
778 if (has_content_area_focus_ == has_focus)
779 return;
781 bool old_plugin_focus = PluginHasFocus();
782 has_content_area_focus_ = has_focus;
783 if (PluginHasFocus() != old_plugin_focus) {
784 instance_interface_->DidChangeFocus(pp_instance(),
785 BoolToPPBool(PluginHasFocus()));
789 void PluginInstance::ViewInitiatedPaint() {
790 if (bound_graphics_2d())
791 bound_graphics_2d()->ViewInitiatedPaint();
792 if (bound_graphics_3d())
793 bound_graphics_3d()->ViewInitiatedPaint();
796 void PluginInstance::ViewFlushedPaint() {
797 // Keep a reference on the stack. See NOTE above.
798 scoped_refptr<PluginInstance> ref(this);
799 if (bound_graphics_2d())
800 bound_graphics_2d()->ViewFlushedPaint();
801 if (bound_graphics_3d())
802 bound_graphics_3d()->ViewFlushedPaint();
805 bool PluginInstance::GetBitmapForOptimizedPluginPaint(
806 const gfx::Rect& paint_bounds,
807 TransportDIB** dib,
808 gfx::Rect* location,
809 gfx::Rect* clip) {
810 if (!always_on_top_)
811 return false;
812 if (!bound_graphics_2d() || !bound_graphics_2d()->is_always_opaque())
813 return false;
815 // We specifically want to compare against the area covered by the backing
816 // store when seeing if we cover the given paint bounds, since the backing
817 // store could be smaller than the declared plugin area.
818 PPB_ImageData_Impl* image_data = bound_graphics_2d()->image_data();
819 gfx::Rect plugin_backing_store_rect(position_.origin(),
820 gfx::Size(image_data->width(),
821 image_data->height()));
822 gfx::Rect clip_page(clip_);
823 clip_page.Offset(position_.origin());
824 gfx::Rect plugin_paint_rect = plugin_backing_store_rect.Intersect(clip_page);
825 if (!plugin_paint_rect.Contains(paint_bounds))
826 return false;
828 *dib = image_data->platform_image()->GetTransportDIB();
829 *location = plugin_backing_store_rect;
830 *clip = clip_page;
831 return true;
834 string16 PluginInstance::GetSelectedText(bool html) {
835 // Keep a reference on the stack. See NOTE above.
836 scoped_refptr<PluginInstance> ref(this);
837 if (!LoadSelectionInterface())
838 return string16();
840 PP_Var rv = plugin_selection_interface_->GetSelectedText(pp_instance(),
841 BoolToPPBool(html));
842 scoped_refptr<StringVar> string(StringVar::FromPPVar(rv));
843 Var::PluginReleasePPVar(rv); // Release the ref the plugin transfered to us.
844 if (!string)
845 return string16();
846 return UTF8ToUTF16(string->value());
849 string16 PluginInstance::GetLinkAtPosition(const gfx::Point& point) {
850 // Keep a reference on the stack. See NOTE above.
851 scoped_refptr<PluginInstance> ref(this);
852 if (!LoadPdfInterface())
853 return string16();
855 PP_Point p;
856 p.x = point.x();
857 p.y = point.y();
858 PP_Var rv = plugin_pdf_interface_->GetLinkAtPosition(pp_instance(), p);
859 scoped_refptr<StringVar> string(StringVar::FromPPVar(rv));
860 Var::PluginReleasePPVar(rv); // Release the ref the plugin transfered to us.
861 if (!string)
862 return string16();
863 return UTF8ToUTF16(string->value());
866 void PluginInstance::Zoom(double factor, bool text_only) {
867 // Keep a reference on the stack. See NOTE above.
868 scoped_refptr<PluginInstance> ref(this);
869 if (!LoadZoomInterface())
870 return;
871 plugin_zoom_interface_->Zoom(pp_instance(), factor, BoolToPPBool(text_only));
874 bool PluginInstance::StartFind(const string16& search_text,
875 bool case_sensitive,
876 int identifier) {
877 // Keep a reference on the stack. See NOTE above.
878 scoped_refptr<PluginInstance> ref(this);
879 if (!LoadFindInterface())
880 return false;
881 find_identifier_ = identifier;
882 return PPBoolToBool(
883 plugin_find_interface_->StartFind(
884 pp_instance(),
885 UTF16ToUTF8(search_text.c_str()).c_str(),
886 BoolToPPBool(case_sensitive)));
889 void PluginInstance::SelectFindResult(bool forward) {
890 // Keep a reference on the stack. See NOTE above.
891 scoped_refptr<PluginInstance> ref(this);
892 if (LoadFindInterface())
893 plugin_find_interface_->SelectFindResult(pp_instance(),
894 BoolToPPBool(forward));
897 void PluginInstance::StopFind() {
898 // Keep a reference on the stack. See NOTE above.
899 scoped_refptr<PluginInstance> ref(this);
900 if (!LoadFindInterface())
901 return;
902 find_identifier_ = -1;
903 plugin_find_interface_->StopFind(pp_instance());
906 bool PluginInstance::LoadFindInterface() {
907 if (!plugin_find_interface_) {
908 plugin_find_interface_ =
909 reinterpret_cast<const PPP_Find_Dev*>(module_->GetPluginInterface(
910 PPP_FIND_DEV_INTERFACE));
913 return !!plugin_find_interface_;
916 bool PluginInstance::LoadMessagingInterface() {
917 if (!checked_for_plugin_messaging_interface_) {
918 checked_for_plugin_messaging_interface_ = true;
919 plugin_messaging_interface_ =
920 reinterpret_cast<const PPP_Messaging_Dev*>(module_->GetPluginInterface(
921 PPP_MESSAGING_DEV_INTERFACE));
924 return !!plugin_messaging_interface_;
927 bool PluginInstance::LoadPdfInterface() {
928 if (!plugin_pdf_interface_) {
929 plugin_pdf_interface_ =
930 reinterpret_cast<const PPP_Pdf*>(module_->GetPluginInterface(
931 PPP_PDF_INTERFACE));
934 return !!plugin_pdf_interface_;
937 bool PluginInstance::LoadSelectionInterface() {
938 if (!plugin_selection_interface_) {
939 plugin_selection_interface_ =
940 reinterpret_cast<const PPP_Selection_Dev*>(module_->GetPluginInterface(
941 PPP_SELECTION_DEV_INTERFACE));
944 return !!plugin_selection_interface_;
947 bool PluginInstance::LoadZoomInterface() {
948 if (!plugin_zoom_interface_) {
949 plugin_zoom_interface_ =
950 reinterpret_cast<const PPP_Zoom_Dev*>(module_->GetPluginInterface(
951 PPP_ZOOM_DEV_INTERFACE));
954 return !!plugin_zoom_interface_;
957 bool PluginInstance::PluginHasFocus() const {
958 return has_webkit_focus_ && has_content_area_focus_;
961 void PluginInstance::ReportGeometry() {
962 // If this call was delayed, we may have transitioned back to fullscreen in
963 // the mean time, so only report the geometry if we are actually in normal
964 // mode.
965 if (container_ && !fullscreen_container_)
966 container_->reportGeometry();
969 bool PluginInstance::GetPreferredPrintOutputFormat(
970 PP_PrintOutputFormat_Dev* format) {
971 // Keep a reference on the stack. See NOTE above.
972 scoped_refptr<PluginInstance> ref(this);
973 if (!plugin_print_interface_) {
974 plugin_print_interface_ =
975 reinterpret_cast<const PPP_Printing_Dev*>(module_->GetPluginInterface(
976 PPP_PRINTING_DEV_INTERFACE));
978 if (!plugin_print_interface_)
979 return false;
980 uint32_t format_count = 0;
981 PP_PrintOutputFormat_Dev* supported_formats =
982 plugin_print_interface_->QuerySupportedFormats(pp_instance(),
983 &format_count);
984 if (!supported_formats)
985 return false;
987 bool found_supported_format = false;
988 for (uint32_t index = 0; index < format_count; index++) {
989 if (supported_formats[index] == PP_PRINTOUTPUTFORMAT_PDF) {
990 // If we found PDF, we are done.
991 found_supported_format = true;
992 *format = PP_PRINTOUTPUTFORMAT_PDF;
993 break;
994 } else if (supported_formats[index] == PP_PRINTOUTPUTFORMAT_RASTER) {
995 // We found raster. Keep looking.
996 found_supported_format = true;
997 *format = PP_PRINTOUTPUTFORMAT_RASTER;
1000 PluginModule::GetCore()->MemFree(supported_formats);
1001 return found_supported_format;
1004 bool PluginInstance::SupportsPrintInterface() {
1005 PP_PrintOutputFormat_Dev format;
1006 return GetPreferredPrintOutputFormat(&format);
1009 int PluginInstance::PrintBegin(const gfx::Rect& printable_area,
1010 int printer_dpi) {
1011 // Keep a reference on the stack. See NOTE above.
1012 scoped_refptr<PluginInstance> ref(this);
1013 PP_PrintOutputFormat_Dev format;
1014 if (!GetPreferredPrintOutputFormat(&format)) {
1015 // PrintBegin should not have been called since SupportsPrintInterface
1016 // would have returned false;
1017 NOTREACHED();
1018 return 0;
1021 PP_PrintSettings_Dev print_settings;
1022 RectToPPRect(printable_area, &print_settings.printable_area);
1023 print_settings.dpi = printer_dpi;
1024 print_settings.orientation = PP_PRINTORIENTATION_NORMAL;
1025 print_settings.grayscale = PP_FALSE;
1026 print_settings.format = format;
1027 int num_pages = plugin_print_interface_->Begin(pp_instance(),
1028 &print_settings);
1029 if (!num_pages)
1030 return 0;
1031 current_print_settings_ = print_settings;
1032 #if defined(OS_LINUX)
1033 canvas_ = NULL;
1034 ranges_.clear();
1035 #endif // defined(OS_LINUX)
1036 return num_pages;
1039 bool PluginInstance::PrintPage(int page_number, WebKit::WebCanvas* canvas) {
1040 DCHECK(plugin_print_interface_);
1041 PP_PrintPageNumberRange_Dev page_range;
1042 page_range.first_page_number = page_range.last_page_number = page_number;
1043 #if defined(OS_LINUX)
1044 ranges_.push_back(page_range);
1045 canvas_ = canvas;
1046 return true;
1047 #else
1048 return PrintPageHelper(&page_range, 1, canvas);
1049 #endif // defined(OS_LINUX)
1052 bool PluginInstance::PrintPageHelper(PP_PrintPageNumberRange_Dev* page_ranges,
1053 int num_ranges,
1054 WebKit::WebCanvas* canvas) {
1055 // Keep a reference on the stack. See NOTE above.
1056 scoped_refptr<PluginInstance> ref(this);
1057 PP_Resource print_output = plugin_print_interface_->PrintPages(
1058 pp_instance(), page_ranges, num_ranges);
1059 if (!print_output)
1060 return false;
1062 bool ret = false;
1064 if (current_print_settings_.format == PP_PRINTOUTPUTFORMAT_PDF)
1065 ret = PrintPDFOutput(print_output, canvas);
1066 else if (current_print_settings_.format == PP_PRINTOUTPUTFORMAT_RASTER)
1067 ret = PrintRasterOutput(print_output, canvas);
1069 // Now we need to release the print output resource.
1070 PluginModule::GetCore()->ReleaseResource(print_output);
1072 return ret;
1075 void PluginInstance::PrintEnd() {
1076 // Keep a reference on the stack. See NOTE above.
1077 scoped_refptr<PluginInstance> ref(this);
1078 #if defined(OS_LINUX)
1079 // This hack is here because all pages need to be written to PDF at once.
1080 if (!ranges_.empty())
1081 PrintPageHelper(&(ranges_.front()), ranges_.size(), canvas_);
1082 canvas_ = NULL;
1083 ranges_.clear();
1084 #endif // defined(OS_LINUX)
1086 DCHECK(plugin_print_interface_);
1087 if (plugin_print_interface_)
1088 plugin_print_interface_->End(pp_instance());
1090 memset(&current_print_settings_, 0, sizeof(current_print_settings_));
1091 #if defined(OS_MACOSX)
1092 last_printed_page_ = NULL;
1093 #endif // defined(OS_MACOSX)
1096 bool PluginInstance::IsFullscreen() {
1097 return fullscreen_;
1100 bool PluginInstance::IsFullscreenOrPending() {
1101 return fullscreen_container_ != NULL;
1104 void PluginInstance::SetFullscreen(bool fullscreen, bool delay_report) {
1105 // Keep a reference on the stack. See NOTE above.
1106 scoped_refptr<PluginInstance> ref(this);
1108 // We check whether we are trying to switch to the state we're already going
1109 // to (i.e. if we're already switching to fullscreen but the fullscreen
1110 // container isn't ready yet, don't do anything more).
1111 if (fullscreen == IsFullscreenOrPending())
1112 return;
1114 BindGraphics(0);
1115 VLOG(1) << "Setting fullscreen to " << (fullscreen ? "on" : "off");
1116 if (fullscreen) {
1117 DCHECK(!fullscreen_container_);
1118 fullscreen_container_ = delegate_->CreateFullscreenContainer(this);
1119 } else {
1120 DCHECK(fullscreen_container_);
1121 fullscreen_container_->Destroy();
1122 fullscreen_container_ = NULL;
1123 fullscreen_ = false;
1124 if (!delay_report) {
1125 ReportGeometry();
1126 } else {
1127 MessageLoop::current()->PostTask(
1128 FROM_HERE, NewRunnableMethod(this, &PluginInstance::ReportGeometry));
1133 bool PluginInstance::NavigateToURL(const char* url, const char* target) {
1134 if (!url || !target || !container_)
1135 return false;
1137 WebDocument document = container_->element().document();
1138 GURL complete_url = document.completeURL(WebString::fromUTF8(url));
1139 // Don't try to deal with the security issues of javascript.
1140 if (complete_url.SchemeIs("javascript"))
1141 return false;
1143 WebURLRequest request(complete_url);
1144 document.frame()->setReferrerForRequest(request, GURL());
1145 request.setHTTPMethod(WebString::fromUTF8("GET"));
1146 request.setFirstPartyForCookies(document.firstPartyForCookies());
1147 request.setHasUserGesture(true);
1149 WebString target_str = WebString::fromUTF8(target);
1150 container_->loadFrameRequest(request, target_str, false, NULL);
1151 return true;
1154 PluginDelegate::PlatformContext3D* PluginInstance::CreateContext3D() {
1155 if (fullscreen_container_)
1156 return fullscreen_container_->CreateContext3D();
1157 else
1158 return delegate_->CreateContext3D();
1161 bool PluginInstance::PrintPDFOutput(PP_Resource print_output,
1162 WebKit::WebCanvas* canvas) {
1163 scoped_refptr<PPB_Buffer_Impl> buffer(
1164 Resource::GetAs<PPB_Buffer_Impl>(print_output));
1165 if (!buffer.get() || !buffer->is_mapped() || !buffer->size()) {
1166 NOTREACHED();
1167 return false;
1169 #if defined(OS_WIN)
1170 // For Windows, we need the PDF DLL to render the output PDF to a DC.
1171 HMODULE pdf_module = GetModuleHandle(L"pdf.dll");
1172 if (!pdf_module)
1173 return false;
1174 RenderPDFPageToDCProc render_proc =
1175 reinterpret_cast<RenderPDFPageToDCProc>(
1176 GetProcAddress(pdf_module, "RenderPDFPageToDC"));
1177 if (!render_proc)
1178 return false;
1179 #endif // defined(OS_WIN)
1181 bool ret = false;
1182 #if defined(OS_LINUX)
1183 // On Linux we just set the final bits in the native metafile.
1184 printing::NativeMetafile* metafile =
1185 printing::NativeMetafileSkiaWrapper::GetMetafileFromCanvas(canvas);
1186 DCHECK(metafile != NULL);
1187 if (metafile)
1188 ret = metafile->InitFromData(buffer->mapped_buffer(), buffer->size());
1189 #elif defined(OS_MACOSX)
1190 // Create a PDF metafile and render from there into the passed in context.
1191 scoped_ptr<printing::NativeMetafile> metafile(
1192 printing::NativeMetafileFactory::CreateFromData(buffer->mapped_buffer(),
1193 buffer->size()));
1194 if (metafile.get() != NULL) {
1195 // Flip the transform.
1196 CGContextSaveGState(canvas);
1197 CGContextTranslateCTM(canvas, 0,
1198 current_print_settings_.printable_area.size.height);
1199 CGContextScaleCTM(canvas, 1.0, -1.0);
1200 CGRect page_rect;
1201 page_rect.origin.x = current_print_settings_.printable_area.point.x;
1202 page_rect.origin.y = current_print_settings_.printable_area.point.y;
1203 page_rect.size.width = current_print_settings_.printable_area.size.width;
1204 page_rect.size.height = current_print_settings_.printable_area.size.height;
1206 ret = metafile->RenderPage(1, canvas, page_rect, true, false, true, true);
1207 CGContextRestoreGState(canvas);
1209 #elif defined(OS_WIN)
1210 // On Windows, we now need to render the PDF to the DC that backs the
1211 // supplied canvas.
1212 skia::VectorPlatformDevice& device =
1213 static_cast<skia::VectorPlatformDevice&>(
1214 canvas->getTopPlatformDevice());
1215 HDC dc = device.getBitmapDC();
1216 gfx::Size size_in_pixels;
1217 size_in_pixels.set_width(
1218 printing::ConvertUnit(current_print_settings_.printable_area.size.width,
1219 static_cast<int>(printing::kPointsPerInch),
1220 current_print_settings_.dpi));
1221 size_in_pixels.set_height(
1222 printing::ConvertUnit(current_print_settings_.printable_area.size.height,
1223 static_cast<int>(printing::kPointsPerInch),
1224 current_print_settings_.dpi));
1225 // We need to render using the actual printer DPI (rendering to a smaller
1226 // set of pixels leads to a blurry output). However, we need to counter the
1227 // scaling up that will happen in the browser.
1228 XFORM xform = {0};
1229 xform.eM11 = xform.eM22 = static_cast<float>(printing::kPointsPerInch) /
1230 static_cast<float>(current_print_settings_.dpi);
1231 ModifyWorldTransform(dc, &xform, MWT_LEFTMULTIPLY);
1233 ret = render_proc(buffer->mapped_buffer(), buffer->size(), 0, dc,
1234 current_print_settings_.dpi, current_print_settings_.dpi,
1235 0, 0, size_in_pixels.width(),
1236 size_in_pixels.height(), true, false, true, true);
1237 #endif // defined(OS_WIN)
1239 return ret;
1242 bool PluginInstance::PrintRasterOutput(PP_Resource print_output,
1243 WebKit::WebCanvas* canvas) {
1244 scoped_refptr<PPB_ImageData_Impl> image(
1245 Resource::GetAs<PPB_ImageData_Impl>(print_output));
1246 if (!image.get() || !image->is_mapped())
1247 return false;
1249 const SkBitmap* bitmap = image->GetMappedBitmap();
1250 if (!bitmap)
1251 return false;
1253 // Draw the printed image into the supplied canvas.
1254 SkIRect src_rect;
1255 src_rect.set(0, 0, bitmap->width(), bitmap->height());
1256 SkRect dest_rect;
1257 dest_rect.set(
1258 SkIntToScalar(current_print_settings_.printable_area.point.x),
1259 SkIntToScalar(current_print_settings_.printable_area.point.y),
1260 SkIntToScalar(current_print_settings_.printable_area.point.x +
1261 current_print_settings_.printable_area.size.width),
1262 SkIntToScalar(current_print_settings_.printable_area.point.y +
1263 current_print_settings_.printable_area.size.height));
1264 bool draw_to_canvas = true;
1265 gfx::Rect dest_rect_gfx;
1266 dest_rect_gfx.set_x(current_print_settings_.printable_area.point.x);
1267 dest_rect_gfx.set_y(current_print_settings_.printable_area.point.y);
1268 dest_rect_gfx.set_width(current_print_settings_.printable_area.size.width);
1269 dest_rect_gfx.set_height(current_print_settings_.printable_area.size.height);
1271 #if defined(OS_WIN)
1272 // Since this is a raster output, the size of the bitmap can be
1273 // huge (especially at high printer DPIs). On Windows, this can
1274 // result in a HUGE EMF (on Mac and Linux the output goes to PDF
1275 // which appears to Flate compress the bitmap). So, if this bitmap
1276 // is larger than 20 MB, we save the bitmap as a JPEG into the EMF
1277 // DC. Note: We chose JPEG over PNG because JPEG compression seems
1278 // way faster (about 4 times faster).
1279 static const int kCompressionThreshold = 20 * 1024 * 1024;
1280 if (bitmap->getSize() > kCompressionThreshold) {
1281 DrawJPEGToPlatformDC(*bitmap, dest_rect_gfx, canvas);
1282 draw_to_canvas = false;
1284 #endif // defined(OS_WIN)
1285 #if defined(OS_MACOSX)
1286 draw_to_canvas = false;
1287 DrawSkBitmapToCanvas(*bitmap, canvas, dest_rect_gfx,
1288 current_print_settings_.printable_area.size.height);
1289 // See comments in the header file.
1290 last_printed_page_ = image;
1291 #else // defined(OS_MACOSX)
1292 if (draw_to_canvas)
1293 canvas->drawBitmapRect(*bitmap, &src_rect, dest_rect);
1294 #endif // defined(OS_MACOSX)
1295 return true;
1298 #if defined(OS_WIN)
1299 bool PluginInstance::DrawJPEGToPlatformDC(
1300 const SkBitmap& bitmap,
1301 const gfx::Rect& printable_area,
1302 WebKit::WebCanvas* canvas) {
1303 skia::VectorPlatformDevice& device =
1304 static_cast<skia::VectorPlatformDevice&>(
1305 canvas->getTopPlatformDevice());
1306 HDC dc = device.getBitmapDC();
1307 // TODO(sanjeevr): This is a temporary hack. If we output a JPEG
1308 // to the EMF, the EnumEnhMetaFile call fails in the browser
1309 // process. The failure also happens if we output nothing here.
1310 // We need to investigate the reason for this failure and fix it.
1311 // In the meantime this temporary hack of drawing an empty
1312 // rectangle in the DC gets us by.
1313 Rectangle(dc, 0, 0, 0, 0);
1315 // Ideally we should add JPEG compression to the VectorPlatformDevice class
1316 // However, Skia currently has no JPEG compression code and we cannot
1317 // depend on gfx/jpeg_codec.h in Skia. So we do the compression here.
1318 SkAutoLockPixels lock(bitmap);
1319 DCHECK(bitmap.getConfig() == SkBitmap::kARGB_8888_Config);
1320 const uint32_t* pixels =
1321 static_cast<const uint32_t*>(bitmap.getPixels());
1322 std::vector<unsigned char> compressed_image;
1323 base::TimeTicks start_time = base::TimeTicks::Now();
1324 bool encoded = gfx::JPEGCodec::Encode(
1325 reinterpret_cast<const unsigned char*>(pixels),
1326 gfx::JPEGCodec::FORMAT_BGRA, bitmap.width(), bitmap.height(),
1327 static_cast<int>(bitmap.rowBytes()), 100, &compressed_image);
1328 UMA_HISTOGRAM_TIMES("PepperPluginPrint.RasterBitmapCompressTime",
1329 base::TimeTicks::Now() - start_time);
1330 if (!encoded) {
1331 NOTREACHED();
1332 return false;
1334 BITMAPINFOHEADER bmi = {0};
1335 gfx::CreateBitmapHeader(bitmap.width(), bitmap.height(), &bmi);
1336 bmi.biCompression = BI_JPEG;
1337 bmi.biSizeImage = compressed_image.size();
1338 bmi.biHeight = -bmi.biHeight;
1339 StretchDIBits(dc, printable_area.x(), printable_area.y(),
1340 printable_area.width(), printable_area.height(),
1341 0, 0, bitmap.width(), bitmap.height(),
1342 &compressed_image.front(),
1343 reinterpret_cast<const BITMAPINFO*>(&bmi),
1344 DIB_RGB_COLORS, SRCCOPY);
1345 return true;
1347 #endif // OS_WIN
1349 #if defined(OS_MACOSX)
1350 void PluginInstance::DrawSkBitmapToCanvas(
1351 const SkBitmap& bitmap, WebKit::WebCanvas* canvas,
1352 const gfx::Rect& dest_rect,
1353 int canvas_height) {
1354 SkAutoLockPixels lock(bitmap);
1355 DCHECK(bitmap.getConfig() == SkBitmap::kARGB_8888_Config);
1356 base::mac::ScopedCFTypeRef<CGDataProviderRef> data_provider(
1357 CGDataProviderCreateWithData(
1358 NULL, bitmap.getAddr32(0, 0),
1359 bitmap.rowBytes() * bitmap.height(), NULL));
1360 base::mac::ScopedCFTypeRef<CGImageRef> image(
1361 CGImageCreate(
1362 bitmap.width(), bitmap.height(),
1363 8, 32, bitmap.rowBytes(),
1364 base::mac::GetSystemColorSpace(),
1365 kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host,
1366 data_provider, NULL, false, kCGRenderingIntentDefault));
1368 // Flip the transform
1369 CGContextSaveGState(canvas);
1370 CGContextTranslateCTM(canvas, 0, canvas_height);
1371 CGContextScaleCTM(canvas, 1.0, -1.0);
1373 CGRect bounds;
1374 bounds.origin.x = dest_rect.x();
1375 bounds.origin.y = canvas_height - dest_rect.y() - dest_rect.height();
1376 bounds.size.width = dest_rect.width();
1377 bounds.size.height = dest_rect.height();
1379 CGContextDrawImage(canvas, bounds, image);
1380 CGContextRestoreGState(canvas);
1382 #endif // defined(OS_MACOSX)
1384 PPB_Graphics2D_Impl* PluginInstance::bound_graphics_2d() const {
1385 if (bound_graphics_.get() == NULL)
1386 return NULL;
1388 return bound_graphics_->Cast<PPB_Graphics2D_Impl>();
1391 PPB_Surface3D_Impl* PluginInstance::bound_graphics_3d() const {
1392 if (bound_graphics_.get() == NULL)
1393 return NULL;
1395 return bound_graphics_->Cast<PPB_Surface3D_Impl>();
1398 void PluginInstance::setBackingTextureId(unsigned int id) {
1399 // If we have a full-screen container_ then the plugin is fullscreen,
1400 // and the parent context is not the one for the browser page, but for the
1401 // full-screen window, and so the parent texture ID doesn't correspond to
1402 // anything in the page's context.
1404 // TODO(alokp): It would be better at some point to have the equivalent
1405 // in the FullscreenContainer so that we don't need to poll
1406 if (fullscreen_container_)
1407 return;
1409 if (container_)
1410 container_->setBackingTextureId(id);
1413 void PluginInstance::AddPluginObject(PluginObject* plugin_object) {
1414 DCHECK(live_plugin_objects_.find(plugin_object) ==
1415 live_plugin_objects_.end());
1416 live_plugin_objects_.insert(plugin_object);
1419 void PluginInstance::RemovePluginObject(PluginObject* plugin_object) {
1420 // Don't actually verify that the object is in the set since during module
1421 // deletion we'll be in the process of freeing them.
1422 live_plugin_objects_.erase(plugin_object);
1425 void PluginInstance::AddNPObjectVar(ObjectVar* object_var) {
1426 DCHECK(np_object_to_object_var_.find(object_var->np_object()) ==
1427 np_object_to_object_var_.end()) << "ObjectVar already in map";
1428 np_object_to_object_var_[object_var->np_object()] = object_var;
1431 void PluginInstance::RemoveNPObjectVar(ObjectVar* object_var) {
1432 NPObjectToObjectVarMap::iterator found =
1433 np_object_to_object_var_.find(object_var->np_object());
1434 if (found == np_object_to_object_var_.end()) {
1435 NOTREACHED() << "ObjectVar not registered.";
1436 return;
1438 if (found->second != object_var) {
1439 NOTREACHED() << "ObjectVar doesn't match.";
1440 return;
1442 np_object_to_object_var_.erase(found);
1445 ObjectVar* PluginInstance::ObjectVarForNPObject(NPObject* np_object) const {
1446 NPObjectToObjectVarMap::const_iterator found =
1447 np_object_to_object_var_.find(np_object);
1448 if (found == np_object_to_object_var_.end())
1449 return NULL;
1450 return found->second;
1453 bool PluginInstance::IsFullPagePlugin() const {
1454 WebFrame* frame = container()->element().document().frame();
1455 return frame->view()->mainFrame()->document().isPluginDocument();
1458 } // namespace ppapi
1459 } // namespace webkit