OLD | NEW |
| (Empty) |
1 // Copyright (c) 2010 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 "webkit/glue/plugins/pepper_plugin_instance.h" | |
6 | |
7 #include "base/logging.h" | |
8 #include "base/metrics/histogram.h" | |
9 #if defined(OS_MACOSX) | |
10 #include "base/mac_util.h" | |
11 #include "base/mac/scoped_cftyperef.h" | |
12 #endif | |
13 #include "base/scoped_ptr.h" | |
14 #include "base/utf_string_conversions.h" | |
15 #include "gfx/rect.h" | |
16 #if defined(OS_WIN) | |
17 #include "gfx/codec/jpeg_codec.h" | |
18 #include "gfx/gdi_util.h" | |
19 #endif | |
20 #include "gfx/skia_util.h" | |
21 #include "ppapi/c/dev/ppb_find_dev.h" | |
22 #include "ppapi/c/dev/ppb_fullscreen_dev.h" | |
23 #include "ppapi/c/dev/ppb_zoom_dev.h" | |
24 #include "ppapi/c/dev/ppp_find_dev.h" | |
25 #include "ppapi/c/dev/ppp_selection_dev.h" | |
26 #include "ppapi/c/dev/ppp_zoom_dev.h" | |
27 #include "ppapi/c/pp_input_event.h" | |
28 #include "ppapi/c/pp_instance.h" | |
29 #include "ppapi/c/pp_rect.h" | |
30 #include "ppapi/c/pp_resource.h" | |
31 #include "ppapi/c/pp_var.h" | |
32 #include "ppapi/c/ppb_core.h" | |
33 #include "ppapi/c/ppb_instance.h" | |
34 #include "ppapi/c/ppp_instance.h" | |
35 #include "printing/native_metafile.h" | |
36 #include "printing/units.h" | |
37 #include "skia/ext/vector_platform_device.h" | |
38 #include "skia/ext/platform_canvas.h" | |
39 #include "third_party/WebKit/WebKit/chromium/public/WebBindings.h" | |
40 #include "third_party/WebKit/WebKit/chromium/public/WebCursorInfo.h" | |
41 #include "third_party/WebKit/WebKit/chromium/public/WebDocument.h" | |
42 #include "third_party/WebKit/WebKit/chromium/public/WebElement.h" | |
43 #include "third_party/WebKit/WebKit/chromium/public/WebFrame.h" | |
44 #include "third_party/WebKit/WebKit/chromium/public/WebInputEvent.h" | |
45 #include "third_party/WebKit/WebKit/chromium/public/WebPluginContainer.h" | |
46 #include "third_party/WebKit/WebKit/chromium/public/WebRect.h" | |
47 #include "third_party/WebKit/WebKit/chromium/public/WebString.h" | |
48 #include "third_party/WebKit/WebKit/chromium/public/WebURLRequest.h" | |
49 #include "third_party/WebKit/WebKit/chromium/public/WebView.h" | |
50 #include "webkit/glue/plugins/pepper_buffer.h" | |
51 #include "webkit/glue/plugins/pepper_common.h" | |
52 #include "webkit/glue/plugins/pepper_graphics_2d.h" | |
53 #include "webkit/glue/plugins/pepper_graphics_3d.h" | |
54 #include "webkit/glue/plugins/pepper_event_conversion.h" | |
55 #include "webkit/glue/plugins/pepper_fullscreen_container.h" | |
56 #include "webkit/glue/plugins/pepper_image_data.h" | |
57 #include "webkit/glue/plugins/pepper_plugin_delegate.h" | |
58 #include "webkit/glue/plugins/pepper_plugin_module.h" | |
59 #include "webkit/glue/plugins/pepper_string.h" | |
60 #include "webkit/glue/plugins/pepper_url_loader.h" | |
61 #include "webkit/glue/plugins/pepper_var.h" | |
62 #include "webkit/glue/plugins/ppp_private.h" | |
63 | |
64 using WebKit::WebBindings; | |
65 using WebKit::WebCanvas; | |
66 using WebKit::WebCursorInfo; | |
67 using WebKit::WebDocument; | |
68 using WebKit::WebFrame; | |
69 using WebKit::WebInputEvent; | |
70 using WebKit::WebPluginContainer; | |
71 using WebKit::WebString; | |
72 using WebKit::WebURLRequest; | |
73 using WebKit::WebView; | |
74 | |
75 namespace pepper { | |
76 | |
77 #if defined(OS_WIN) | |
78 // Exported by pdf.dll | |
79 typedef bool (*RenderPDFPageToDCProc)( | |
80 const unsigned char* pdf_buffer, int buffer_size, int page_number, HDC dc, | |
81 int dpi_x, int dpi_y, int bounds_origin_x, int bounds_origin_y, | |
82 int bounds_width, int bounds_height, bool fit_to_bounds, | |
83 bool stretch_to_bounds, bool keep_aspect_ratio, bool center_in_bounds); | |
84 #endif // defined(OS_WIN) | |
85 | |
86 namespace { | |
87 | |
88 #define COMPILE_ASSERT_MATCHING_ENUM(webkit_name, np_name) \ | |
89 COMPILE_ASSERT(int(WebCursorInfo::webkit_name) == int(np_name), \ | |
90 mismatching_enums) | |
91 | |
92 COMPILE_ASSERT_MATCHING_ENUM(TypePointer, PP_CURSORTYPE_POINTER); | |
93 COMPILE_ASSERT_MATCHING_ENUM(TypeCross, PP_CURSORTYPE_CROSS); | |
94 COMPILE_ASSERT_MATCHING_ENUM(TypeHand, PP_CURSORTYPE_HAND); | |
95 COMPILE_ASSERT_MATCHING_ENUM(TypeIBeam, PP_CURSORTYPE_IBEAM); | |
96 COMPILE_ASSERT_MATCHING_ENUM(TypeWait, PP_CURSORTYPE_WAIT); | |
97 COMPILE_ASSERT_MATCHING_ENUM(TypeHelp, PP_CURSORTYPE_HELP); | |
98 COMPILE_ASSERT_MATCHING_ENUM(TypeEastResize, PP_CURSORTYPE_EASTRESIZE); | |
99 COMPILE_ASSERT_MATCHING_ENUM(TypeNorthResize, PP_CURSORTYPE_NORTHRESIZE); | |
100 COMPILE_ASSERT_MATCHING_ENUM(TypeNorthEastResize, | |
101 PP_CURSORTYPE_NORTHEASTRESIZE); | |
102 COMPILE_ASSERT_MATCHING_ENUM(TypeNorthWestResize, | |
103 PP_CURSORTYPE_NORTHWESTRESIZE); | |
104 COMPILE_ASSERT_MATCHING_ENUM(TypeSouthResize, PP_CURSORTYPE_SOUTHRESIZE); | |
105 COMPILE_ASSERT_MATCHING_ENUM(TypeSouthEastResize, | |
106 PP_CURSORTYPE_SOUTHEASTRESIZE); | |
107 COMPILE_ASSERT_MATCHING_ENUM(TypeSouthWestResize, | |
108 PP_CURSORTYPE_SOUTHWESTRESIZE); | |
109 COMPILE_ASSERT_MATCHING_ENUM(TypeWestResize, PP_CURSORTYPE_WESTRESIZE); | |
110 COMPILE_ASSERT_MATCHING_ENUM(TypeNorthSouthResize, | |
111 PP_CURSORTYPE_NORTHSOUTHRESIZE); | |
112 COMPILE_ASSERT_MATCHING_ENUM(TypeEastWestResize, PP_CURSORTYPE_EASTWESTRESIZE); | |
113 COMPILE_ASSERT_MATCHING_ENUM(TypeNorthEastSouthWestResize, | |
114 PP_CURSORTYPE_NORTHEASTSOUTHWESTRESIZE); | |
115 COMPILE_ASSERT_MATCHING_ENUM(TypeNorthWestSouthEastResize, | |
116 PP_CURSORTYPE_NORTHWESTSOUTHEASTRESIZE); | |
117 COMPILE_ASSERT_MATCHING_ENUM(TypeColumnResize, PP_CURSORTYPE_COLUMNRESIZE); | |
118 COMPILE_ASSERT_MATCHING_ENUM(TypeRowResize, PP_CURSORTYPE_ROWRESIZE); | |
119 COMPILE_ASSERT_MATCHING_ENUM(TypeMiddlePanning, PP_CURSORTYPE_MIDDLEPANNING); | |
120 COMPILE_ASSERT_MATCHING_ENUM(TypeEastPanning, PP_CURSORTYPE_EASTPANNING); | |
121 COMPILE_ASSERT_MATCHING_ENUM(TypeNorthPanning, PP_CURSORTYPE_NORTHPANNING); | |
122 COMPILE_ASSERT_MATCHING_ENUM(TypeNorthEastPanning, | |
123 PP_CURSORTYPE_NORTHEASTPANNING); | |
124 COMPILE_ASSERT_MATCHING_ENUM(TypeNorthWestPanning, | |
125 PP_CURSORTYPE_NORTHWESTPANNING); | |
126 COMPILE_ASSERT_MATCHING_ENUM(TypeSouthPanning, PP_CURSORTYPE_SOUTHPANNING); | |
127 COMPILE_ASSERT_MATCHING_ENUM(TypeSouthEastPanning, | |
128 PP_CURSORTYPE_SOUTHEASTPANNING); | |
129 COMPILE_ASSERT_MATCHING_ENUM(TypeSouthWestPanning, | |
130 PP_CURSORTYPE_SOUTHWESTPANNING); | |
131 COMPILE_ASSERT_MATCHING_ENUM(TypeWestPanning, PP_CURSORTYPE_WESTPANNING); | |
132 COMPILE_ASSERT_MATCHING_ENUM(TypeMove, PP_CURSORTYPE_MOVE); | |
133 COMPILE_ASSERT_MATCHING_ENUM(TypeVerticalText, PP_CURSORTYPE_VERTICALTEXT); | |
134 COMPILE_ASSERT_MATCHING_ENUM(TypeCell, PP_CURSORTYPE_CELL); | |
135 COMPILE_ASSERT_MATCHING_ENUM(TypeContextMenu, PP_CURSORTYPE_CONTEXTMENU); | |
136 COMPILE_ASSERT_MATCHING_ENUM(TypeAlias, PP_CURSORTYPE_ALIAS); | |
137 COMPILE_ASSERT_MATCHING_ENUM(TypeProgress, PP_CURSORTYPE_PROGRESS); | |
138 COMPILE_ASSERT_MATCHING_ENUM(TypeNoDrop, PP_CURSORTYPE_NODROP); | |
139 COMPILE_ASSERT_MATCHING_ENUM(TypeCopy, PP_CURSORTYPE_COPY); | |
140 COMPILE_ASSERT_MATCHING_ENUM(TypeNone, PP_CURSORTYPE_NONE); | |
141 COMPILE_ASSERT_MATCHING_ENUM(TypeNotAllowed, PP_CURSORTYPE_NOTALLOWED); | |
142 COMPILE_ASSERT_MATCHING_ENUM(TypeZoomIn, PP_CURSORTYPE_ZOOMIN); | |
143 COMPILE_ASSERT_MATCHING_ENUM(TypeZoomOut, PP_CURSORTYPE_ZOOMOUT); | |
144 COMPILE_ASSERT_MATCHING_ENUM(TypeCustom, PP_CURSORTYPE_CUSTOM); | |
145 | |
146 void RectToPPRect(const gfx::Rect& input, PP_Rect* output) { | |
147 *output = PP_MakeRectFromXYWH(input.x(), input.y(), | |
148 input.width(), input.height()); | |
149 } | |
150 | |
151 PP_Var GetWindowObject(PP_Instance instance_id) { | |
152 PluginInstance* instance = ResourceTracker::Get()->GetInstance(instance_id); | |
153 if (!instance) | |
154 return PP_MakeUndefined(); | |
155 return instance->GetWindowObject(); | |
156 } | |
157 | |
158 PP_Var GetOwnerElementObject(PP_Instance instance_id) { | |
159 PluginInstance* instance = ResourceTracker::Get()->GetInstance(instance_id); | |
160 if (!instance) | |
161 return PP_MakeUndefined(); | |
162 return instance->GetOwnerElementObject(); | |
163 } | |
164 | |
165 PP_Bool BindGraphics(PP_Instance instance_id, PP_Resource graphics_id) { | |
166 PluginInstance* instance = ResourceTracker::Get()->GetInstance(instance_id); | |
167 if (!instance) | |
168 return PP_FALSE; | |
169 return BoolToPPBool(instance->BindGraphics(graphics_id)); | |
170 } | |
171 | |
172 PP_Bool IsFullFrame(PP_Instance instance_id) { | |
173 PluginInstance* instance = ResourceTracker::Get()->GetInstance(instance_id); | |
174 if (!instance) | |
175 return PP_FALSE; | |
176 return BoolToPPBool(instance->full_frame()); | |
177 } | |
178 | |
179 PP_Var ExecuteScript(PP_Instance instance_id, | |
180 PP_Var script, | |
181 PP_Var* exception) { | |
182 PluginInstance* instance = ResourceTracker::Get()->GetInstance(instance_id); | |
183 if (!instance) | |
184 return PP_MakeUndefined(); | |
185 return instance->ExecuteScript(script, exception); | |
186 } | |
187 | |
188 const PPB_Instance ppb_instance = { | |
189 &GetWindowObject, | |
190 &GetOwnerElementObject, | |
191 &BindGraphics, | |
192 &IsFullFrame, | |
193 &ExecuteScript, | |
194 }; | |
195 | |
196 void NumberOfFindResultsChanged(PP_Instance instance_id, | |
197 int32_t total, | |
198 PP_Bool final_result) { | |
199 PluginInstance* instance = ResourceTracker::Get()->GetInstance(instance_id); | |
200 if (!instance) | |
201 return; | |
202 | |
203 DCHECK_NE(instance->find_identifier(), -1); | |
204 instance->delegate()->NumberOfFindResultsChanged( | |
205 instance->find_identifier(), total, PPBoolToBool(final_result)); | |
206 } | |
207 | |
208 void SelectedFindResultChanged(PP_Instance instance_id, | |
209 int32_t index) { | |
210 PluginInstance* instance = ResourceTracker::Get()->GetInstance(instance_id); | |
211 if (!instance) | |
212 return; | |
213 | |
214 DCHECK_NE(instance->find_identifier(), -1); | |
215 instance->delegate()->SelectedFindResultChanged( | |
216 instance->find_identifier(), index); | |
217 } | |
218 | |
219 const PPB_Find_Dev ppb_find = { | |
220 &NumberOfFindResultsChanged, | |
221 &SelectedFindResultChanged, | |
222 }; | |
223 | |
224 PP_Bool IsFullscreen(PP_Instance instance_id) { | |
225 PluginInstance* instance = ResourceTracker::Get()->GetInstance(instance_id); | |
226 if (!instance) | |
227 return PP_FALSE; | |
228 return BoolToPPBool(instance->IsFullscreen()); | |
229 } | |
230 | |
231 PP_Bool SetFullscreen(PP_Instance instance_id, PP_Bool fullscreen) { | |
232 PluginInstance* instance = ResourceTracker::Get()->GetInstance(instance_id); | |
233 if (!instance) | |
234 return PP_FALSE; | |
235 return BoolToPPBool(instance->SetFullscreen(PPBoolToBool(fullscreen))); | |
236 } | |
237 | |
238 const PPB_Fullscreen_Dev ppb_fullscreen = { | |
239 &IsFullscreen, | |
240 &SetFullscreen, | |
241 }; | |
242 | |
243 void ZoomChanged(PP_Instance instance_id, double factor) { | |
244 PluginInstance* instance = ResourceTracker::Get()->GetInstance(instance_id); | |
245 if (!instance) | |
246 return; | |
247 | |
248 // We only want to tell the page to change its zoom if the whole page is the | |
249 // PDF. If we're in an iframe, then don't do anything. | |
250 WebFrame* frame = instance->container()->element().document().frame(); | |
251 if (!frame->view()->mainFrame()->document().isPluginDocument()) | |
252 return; | |
253 | |
254 double zoom_level = WebView::zoomFactorToZoomLevel(factor); | |
255 // The conversino from zoom level to factor, and back, can introduce rounding | |
256 // errors. i.e. WebKit originally tells us 3.0, but by the time we tell the | |
257 // plugin and it tells us back, the level becomes 3.000000000004. Need to | |
258 // round or else otherwise if the user zooms out, it will go to 3.0 instead of | |
259 // 2.0. | |
260 int rounded = | |
261 static_cast<int>(zoom_level + (zoom_level > 0 ? 0.001 : -0.001)); | |
262 if (abs(rounded - zoom_level) < 0.001) | |
263 zoom_level = rounded; | |
264 instance->container()->zoomLevelChanged(zoom_level); | |
265 } | |
266 | |
267 void ZoomLimitsChanged(PP_Instance instance_id, | |
268 double minimum_factor, | |
269 double maximium_factor) { | |
270 if (minimum_factor > maximium_factor) { | |
271 NOTREACHED(); | |
272 return; | |
273 } | |
274 | |
275 PluginInstance* instance = ResourceTracker::Get()->GetInstance(instance_id); | |
276 if (!instance) | |
277 return; | |
278 instance->delegate()->ZoomLimitsChanged(minimum_factor, maximium_factor); | |
279 } | |
280 | |
281 const PPB_Zoom_Dev ppb_zoom = { | |
282 &ZoomChanged, | |
283 &ZoomLimitsChanged | |
284 }; | |
285 | |
286 } // namespace | |
287 | |
288 PluginInstance::PluginInstance(PluginDelegate* delegate, | |
289 PluginModule* module, | |
290 const PPP_Instance* instance_interface) | |
291 : delegate_(delegate), | |
292 module_(module), | |
293 instance_interface_(instance_interface), | |
294 pp_instance_(0), | |
295 container_(NULL), | |
296 full_frame_(false), | |
297 has_webkit_focus_(false), | |
298 has_content_area_focus_(false), | |
299 find_identifier_(-1), | |
300 plugin_find_interface_(NULL), | |
301 plugin_private_interface_(NULL), | |
302 plugin_selection_interface_(NULL), | |
303 plugin_zoom_interface_(NULL), | |
304 #if defined (OS_LINUX) | |
305 num_pages_(0), | |
306 pdf_output_done_(false), | |
307 #endif // defined (OS_LINUX) | |
308 plugin_print_interface_(NULL), | |
309 plugin_graphics_3d_interface_(NULL), | |
310 always_on_top_(false), | |
311 fullscreen_container_(NULL) { | |
312 pp_instance_ = ResourceTracker::Get()->AddInstance(this); | |
313 | |
314 memset(¤t_print_settings_, 0, sizeof(current_print_settings_)); | |
315 DCHECK(delegate); | |
316 module_->InstanceCreated(this); | |
317 delegate_->InstanceCreated(this); | |
318 } | |
319 | |
320 PluginInstance::~PluginInstance() { | |
321 FOR_EACH_OBSERVER(Observer, observers_, InstanceDestroyed(this)); | |
322 | |
323 delegate_->InstanceDeleted(this); | |
324 module_->InstanceDeleted(this); | |
325 | |
326 ResourceTracker::Get()->InstanceDeleted(pp_instance_); | |
327 } | |
328 | |
329 // static | |
330 const PPB_Instance* PluginInstance::GetInterface() { | |
331 return &ppb_instance; | |
332 } | |
333 | |
334 // static | |
335 const PPB_Find_Dev* PluginInstance::GetFindInterface() { | |
336 return &ppb_find; | |
337 } | |
338 | |
339 // static | |
340 const PPB_Fullscreen_Dev* PluginInstance::GetFullscreenInterface() { | |
341 return &ppb_fullscreen; | |
342 } | |
343 | |
344 // static | |
345 const PPB_Zoom_Dev* PluginInstance::GetZoomInterface() { | |
346 return &ppb_zoom; | |
347 } | |
348 | |
349 void PluginInstance::AddObserver(Observer* observer) { | |
350 observers_.AddObserver(observer); | |
351 } | |
352 | |
353 void PluginInstance::RemoveObserver(Observer* observer) { | |
354 observers_.RemoveObserver(observer); | |
355 } | |
356 | |
357 void PluginInstance::Paint(WebCanvas* canvas, | |
358 const gfx::Rect& plugin_rect, | |
359 const gfx::Rect& paint_rect) { | |
360 if (bound_graphics_2d()) | |
361 bound_graphics_2d()->Paint(canvas, plugin_rect, paint_rect); | |
362 } | |
363 | |
364 void PluginInstance::InvalidateRect(const gfx::Rect& rect) { | |
365 if (fullscreen_container_) { | |
366 if (rect.IsEmpty()) | |
367 fullscreen_container_->Invalidate(); | |
368 else | |
369 fullscreen_container_->InvalidateRect(rect); | |
370 } else { | |
371 if (!container_ || position_.IsEmpty()) | |
372 return; // Nothing to do. | |
373 if (rect.IsEmpty()) | |
374 container_->invalidate(); | |
375 else | |
376 container_->invalidateRect(rect); | |
377 } | |
378 } | |
379 | |
380 void PluginInstance::ScrollRect(int dx, int dy, const gfx::Rect& rect) { | |
381 if (fullscreen_container_) { | |
382 fullscreen_container_->ScrollRect(dx, dy, rect); | |
383 } else { | |
384 if (full_frame_) { | |
385 container_->scrollRect(dx, dy, rect); | |
386 } else { | |
387 // Can't do optimized scrolling since there could be other elements on top | |
388 // of us. | |
389 InvalidateRect(rect); | |
390 } | |
391 } | |
392 } | |
393 | |
394 unsigned PluginInstance::GetBackingTextureId() { | |
395 if (!bound_graphics_3d()) | |
396 return 0; | |
397 | |
398 return bound_graphics_3d()->GetBackingTextureId(); | |
399 } | |
400 | |
401 void PluginInstance::CommitBackingTexture() { | |
402 container_->commitBackingTexture(); | |
403 } | |
404 | |
405 PP_Var PluginInstance::GetWindowObject() { | |
406 if (!container_) | |
407 return PP_MakeUndefined(); | |
408 | |
409 WebFrame* frame = container_->element().document().frame(); | |
410 if (!frame) | |
411 return PP_MakeUndefined(); | |
412 | |
413 return ObjectVar::NPObjectToPPVar(module(), frame->windowObject()); | |
414 } | |
415 | |
416 PP_Var PluginInstance::GetOwnerElementObject() { | |
417 if (!container_) | |
418 return PP_MakeUndefined(); | |
419 return ObjectVar::NPObjectToPPVar(module(), | |
420 container_->scriptableObjectForElement()); | |
421 } | |
422 | |
423 bool PluginInstance::BindGraphics(PP_Resource graphics_id) { | |
424 if (!graphics_id) { | |
425 // Special-case clearing the current device. | |
426 if (bound_graphics_.get()) { | |
427 if (bound_graphics_2d()) { | |
428 bound_graphics_2d()->BindToInstance(NULL); | |
429 } else if (bound_graphics_.get()) { | |
430 bound_graphics_3d()->SetSwapBuffersCallback(NULL); | |
431 bound_graphics_3d()->BindToInstance(NULL); | |
432 } | |
433 InvalidateRect(gfx::Rect()); | |
434 } | |
435 bound_graphics_ = NULL; | |
436 return true; | |
437 } | |
438 | |
439 scoped_refptr<Graphics2D> graphics_2d = | |
440 Resource::GetAs<Graphics2D>(graphics_id); | |
441 scoped_refptr<Graphics3D> graphics_3d = | |
442 Resource::GetAs<Graphics3D>(graphics_id); | |
443 | |
444 if (graphics_2d) { | |
445 if (!graphics_2d->BindToInstance(this)) | |
446 return false; // Can't bind to more than one instance. | |
447 | |
448 // See http://crbug.com/49403: this can be further optimized by keeping the | |
449 // old device around and painting from it. | |
450 if (bound_graphics_2d()) { | |
451 // Start the new image with the content of the old image until the plugin | |
452 // repaints. | |
453 const SkBitmap* old_backing_bitmap = | |
454 bound_graphics_2d()->image_data()->GetMappedBitmap(); | |
455 SkRect old_size = SkRect::MakeWH( | |
456 SkScalar(static_cast<float>(old_backing_bitmap->width())), | |
457 SkScalar(static_cast<float>(old_backing_bitmap->height()))); | |
458 | |
459 SkCanvas canvas(*graphics_2d->image_data()->GetMappedBitmap()); | |
460 canvas.drawBitmap(*old_backing_bitmap, 0, 0); | |
461 | |
462 // Fill in any extra space with white. | |
463 canvas.clipRect(old_size, SkRegion::kDifference_Op); | |
464 canvas.drawARGB(255, 255, 255, 255); | |
465 } | |
466 | |
467 bound_graphics_ = graphics_2d; | |
468 // BindToInstance will have invalidated the plugin if necessary. | |
469 } else if (graphics_3d) { | |
470 if (!graphics_3d->BindToInstance(this)) | |
471 return false; | |
472 | |
473 bound_graphics_ = graphics_3d; | |
474 bound_graphics_3d()->SetSwapBuffersCallback( | |
475 NewCallback(this, &PluginInstance::CommitBackingTexture)); | |
476 } | |
477 | |
478 return true; | |
479 } | |
480 | |
481 bool PluginInstance::SetCursor(PP_CursorType_Dev type) { | |
482 cursor_.reset(new WebCursorInfo(static_cast<WebCursorInfo::Type>(type))); | |
483 return true; | |
484 } | |
485 | |
486 PP_Var PluginInstance::ExecuteScript(PP_Var script, PP_Var* exception) { | |
487 TryCatch try_catch(module(), exception); | |
488 if (try_catch.has_exception()) | |
489 return PP_MakeUndefined(); | |
490 | |
491 // Convert the script into an inconvenient NPString object. | |
492 scoped_refptr<StringVar> script_string(StringVar::FromPPVar(script)); | |
493 if (!script_string) { | |
494 try_catch.SetException("Script param to ExecuteScript must be a string."); | |
495 return PP_MakeUndefined(); | |
496 } | |
497 NPString np_script; | |
498 np_script.UTF8Characters = script_string->value().c_str(); | |
499 np_script.UTF8Length = script_string->value().length(); | |
500 | |
501 // Get the current frame to pass to the evaluate function. | |
502 WebFrame* frame = container_->element().document().frame(); | |
503 if (!frame) { | |
504 try_catch.SetException("No frame to execute script in."); | |
505 return PP_MakeUndefined(); | |
506 } | |
507 | |
508 NPVariant result; | |
509 bool ok = WebBindings::evaluate(NULL, frame->windowObject(), &np_script, | |
510 &result); | |
511 if (!ok) { | |
512 // TODO(brettw) bug 54011: The TryCatch isn't working properly and | |
513 // doesn't actually catch this exception. | |
514 try_catch.SetException("Exception caught"); | |
515 WebBindings::releaseVariantValue(&result); | |
516 return PP_MakeUndefined(); | |
517 } | |
518 | |
519 PP_Var ret = Var::NPVariantToPPVar(module_, &result); | |
520 WebBindings::releaseVariantValue(&result); | |
521 return ret; | |
522 } | |
523 | |
524 void PluginInstance::Delete() { | |
525 instance_interface_->DidDestroy(pp_instance()); | |
526 | |
527 if (fullscreen_container_) { | |
528 fullscreen_container_->Destroy(); | |
529 fullscreen_container_ = NULL; | |
530 } | |
531 container_ = NULL; | |
532 } | |
533 | |
534 bool PluginInstance::Initialize(WebPluginContainer* container, | |
535 const std::vector<std::string>& arg_names, | |
536 const std::vector<std::string>& arg_values, | |
537 bool full_frame) { | |
538 container_ = container; | |
539 full_frame_ = full_frame; | |
540 | |
541 size_t argc = 0; | |
542 scoped_array<const char*> argn(new const char*[arg_names.size()]); | |
543 scoped_array<const char*> argv(new const char*[arg_names.size()]); | |
544 for (size_t i = 0; i < arg_names.size(); ++i) { | |
545 argn[argc] = arg_names[i].c_str(); | |
546 argv[argc] = arg_values[i].c_str(); | |
547 argc++; | |
548 } | |
549 | |
550 return PPBoolToBool(instance_interface_->DidCreate(pp_instance(), | |
551 argc, | |
552 argn.get(), | |
553 argv.get())); | |
554 } | |
555 | |
556 bool PluginInstance::HandleDocumentLoad(URLLoader* loader) { | |
557 Resource::ScopedResourceId resource(loader); | |
558 return PPBoolToBool(instance_interface_->HandleDocumentLoad(pp_instance(), | |
559 resource.id)); | |
560 } | |
561 | |
562 bool PluginInstance::HandleInputEvent(const WebKit::WebInputEvent& event, | |
563 WebCursorInfo* cursor_info) { | |
564 std::vector<PP_InputEvent> pp_events; | |
565 CreatePPEvent(event, &pp_events); | |
566 | |
567 // Each input event may generate more than one PP_InputEvent. | |
568 bool rv = false; | |
569 for (size_t i = 0; i < pp_events.size(); i++) { | |
570 rv |= PPBoolToBool(instance_interface_->HandleInputEvent(pp_instance(), | |
571 &pp_events[i])); | |
572 } | |
573 | |
574 if (cursor_.get()) | |
575 *cursor_info = *cursor_; | |
576 return rv; | |
577 } | |
578 | |
579 PP_Var PluginInstance::GetInstanceObject() { | |
580 return instance_interface_->GetInstanceObject(pp_instance()); | |
581 } | |
582 | |
583 void PluginInstance::ViewChanged(const gfx::Rect& position, | |
584 const gfx::Rect& clip) { | |
585 if (position.size() != position_.size() && bound_graphics_3d()) { | |
586 // TODO(apatrick): This is a hack to force the back buffer to resize. | |
587 // It is obviously wrong to call SwapBuffers when a partial frame has | |
588 // potentially been rendered. Plan is to embed resize commands in the | |
589 // command buffer just before ViewChanged is called. | |
590 bound_graphics_3d()->ResizeBackingTexture(position.size()); | |
591 bound_graphics_3d()->SwapBuffers(); | |
592 } | |
593 | |
594 position_ = position; | |
595 | |
596 if (clip.IsEmpty()) { | |
597 // WebKit can give weird (x,y) positions for empty clip rects (since the | |
598 // position technically doesn't matter). But we want to make these | |
599 // consistent since this is given to the plugin, so force everything to 0 | |
600 // in the "everything is clipped" case. | |
601 clip_ = gfx::Rect(); | |
602 } else { | |
603 clip_ = clip; | |
604 } | |
605 | |
606 PP_Rect pp_position, pp_clip; | |
607 RectToPPRect(position_, &pp_position); | |
608 RectToPPRect(clip_, &pp_clip); | |
609 instance_interface_->DidChangeView(pp_instance(), &pp_position, &pp_clip); | |
610 } | |
611 | |
612 void PluginInstance::SetWebKitFocus(bool has_focus) { | |
613 if (has_webkit_focus_ == has_focus) | |
614 return; | |
615 | |
616 bool old_plugin_focus = PluginHasFocus(); | |
617 has_webkit_focus_ = has_focus; | |
618 if (PluginHasFocus() != old_plugin_focus) { | |
619 instance_interface_->DidChangeFocus(pp_instance(), | |
620 BoolToPPBool(PluginHasFocus())); | |
621 } | |
622 } | |
623 | |
624 void PluginInstance::SetContentAreaFocus(bool has_focus) { | |
625 if (has_content_area_focus_ == has_focus) | |
626 return; | |
627 | |
628 bool old_plugin_focus = PluginHasFocus(); | |
629 has_content_area_focus_ = has_focus; | |
630 if (PluginHasFocus() != old_plugin_focus) { | |
631 instance_interface_->DidChangeFocus(pp_instance(), | |
632 BoolToPPBool(PluginHasFocus())); | |
633 } | |
634 } | |
635 | |
636 void PluginInstance::ViewInitiatedPaint() { | |
637 if (bound_graphics_2d()) | |
638 bound_graphics_2d()->ViewInitiatedPaint(); | |
639 } | |
640 | |
641 void PluginInstance::ViewFlushedPaint() { | |
642 if (bound_graphics_2d()) | |
643 bound_graphics_2d()->ViewFlushedPaint(); | |
644 } | |
645 | |
646 bool PluginInstance::GetBitmapForOptimizedPluginPaint( | |
647 const gfx::Rect& paint_bounds, | |
648 TransportDIB** dib, | |
649 gfx::Rect* location, | |
650 gfx::Rect* clip) { | |
651 if (!always_on_top_) | |
652 return false; | |
653 if (!bound_graphics_2d() || !bound_graphics_2d()->is_always_opaque()) | |
654 return false; | |
655 | |
656 // We specifically want to compare against the area covered by the backing | |
657 // store when seeing if we cover the given paint bounds, since the backing | |
658 // store could be smaller than the declared plugin area. | |
659 ImageData* image_data = bound_graphics_2d()->image_data(); | |
660 gfx::Rect plugin_backing_store_rect(position_.origin(), | |
661 gfx::Size(image_data->width(), | |
662 image_data->height())); | |
663 gfx::Rect clip_page(clip_); | |
664 clip_page.Offset(position_.origin()); | |
665 gfx::Rect plugin_paint_rect = plugin_backing_store_rect.Intersect(clip_page); | |
666 if (!plugin_paint_rect.Contains(paint_bounds)) | |
667 return false; | |
668 | |
669 *dib = image_data->platform_image()->GetTransportDIB(); | |
670 *location = plugin_backing_store_rect; | |
671 *clip = clip_page; | |
672 return true; | |
673 } | |
674 | |
675 string16 PluginInstance::GetSelectedText(bool html) { | |
676 if (!LoadSelectionInterface()) | |
677 return string16(); | |
678 | |
679 PP_Var rv = plugin_selection_interface_->GetSelectedText(pp_instance(), | |
680 BoolToPPBool(html)); | |
681 scoped_refptr<StringVar> string(StringVar::FromPPVar(rv)); | |
682 Var::PluginReleasePPVar(rv); // Release the ref the plugin transfered to us. | |
683 if (!string) | |
684 return string16(); | |
685 return UTF8ToUTF16(string->value()); | |
686 } | |
687 | |
688 string16 PluginInstance::GetLinkAtPosition(const gfx::Point& point) { | |
689 if (!LoadPrivateInterface()) | |
690 return string16(); | |
691 | |
692 PP_Point p; | |
693 p.x = point.x(); | |
694 p.y = point.y(); | |
695 PP_Var rv = plugin_private_interface_->GetLinkAtPosition(pp_instance(), p); | |
696 scoped_refptr<StringVar> string(StringVar::FromPPVar(rv)); | |
697 Var::PluginReleasePPVar(rv); // Release the ref the plugin transfered to us. | |
698 if (!string) | |
699 return string16(); | |
700 return UTF8ToUTF16(string->value()); | |
701 } | |
702 | |
703 void PluginInstance::Zoom(double factor, bool text_only) { | |
704 if (!LoadZoomInterface()) | |
705 return; | |
706 plugin_zoom_interface_->Zoom(pp_instance(), factor, BoolToPPBool(text_only)); | |
707 } | |
708 | |
709 bool PluginInstance::StartFind(const string16& search_text, | |
710 bool case_sensitive, | |
711 int identifier) { | |
712 if (!LoadFindInterface()) | |
713 return false; | |
714 find_identifier_ = identifier; | |
715 return PPBoolToBool( | |
716 plugin_find_interface_->StartFind( | |
717 pp_instance(), | |
718 UTF16ToUTF8(search_text.c_str()).c_str(), | |
719 BoolToPPBool(case_sensitive))); | |
720 } | |
721 | |
722 void PluginInstance::SelectFindResult(bool forward) { | |
723 if (LoadFindInterface()) | |
724 plugin_find_interface_->SelectFindResult(pp_instance(), | |
725 BoolToPPBool(forward)); | |
726 } | |
727 | |
728 void PluginInstance::StopFind() { | |
729 if (!LoadFindInterface()) | |
730 return; | |
731 find_identifier_ = -1; | |
732 plugin_find_interface_->StopFind(pp_instance()); | |
733 } | |
734 | |
735 bool PluginInstance::LoadFindInterface() { | |
736 if (!plugin_find_interface_) { | |
737 plugin_find_interface_ = | |
738 reinterpret_cast<const PPP_Find_Dev*>(module_->GetPluginInterface( | |
739 PPP_FIND_DEV_INTERFACE)); | |
740 } | |
741 | |
742 return !!plugin_find_interface_; | |
743 } | |
744 | |
745 bool PluginInstance::LoadPrivateInterface() { | |
746 if (!plugin_private_interface_) { | |
747 plugin_private_interface_ = | |
748 reinterpret_cast<const PPP_Private*>(module_->GetPluginInterface( | |
749 PPP_PRIVATE_INTERFACE)); | |
750 } | |
751 | |
752 return !!plugin_private_interface_; | |
753 } | |
754 | |
755 bool PluginInstance::LoadSelectionInterface() { | |
756 if (!plugin_selection_interface_) { | |
757 plugin_selection_interface_ = | |
758 reinterpret_cast<const PPP_Selection_Dev*>(module_->GetPluginInterface( | |
759 PPP_SELECTION_DEV_INTERFACE)); | |
760 } | |
761 | |
762 return !!plugin_selection_interface_; | |
763 } | |
764 | |
765 bool PluginInstance::LoadZoomInterface() { | |
766 if (!plugin_zoom_interface_) { | |
767 plugin_zoom_interface_ = | |
768 reinterpret_cast<const PPP_Zoom_Dev*>(module_->GetPluginInterface( | |
769 PPP_ZOOM_DEV_INTERFACE)); | |
770 } | |
771 | |
772 return !!plugin_zoom_interface_; | |
773 } | |
774 | |
775 bool PluginInstance::PluginHasFocus() const { | |
776 return has_webkit_focus_ && has_content_area_focus_; | |
777 } | |
778 | |
779 bool PluginInstance::GetPreferredPrintOutputFormat( | |
780 PP_PrintOutputFormat_Dev* format) { | |
781 if (!plugin_print_interface_) { | |
782 plugin_print_interface_ = | |
783 reinterpret_cast<const PPP_Printing_Dev*>(module_->GetPluginInterface( | |
784 PPP_PRINTING_DEV_INTERFACE)); | |
785 } | |
786 if (!plugin_print_interface_) | |
787 return false; | |
788 uint32_t format_count = 0; | |
789 PP_PrintOutputFormat_Dev* supported_formats = | |
790 plugin_print_interface_->QuerySupportedFormats(pp_instance(), | |
791 &format_count); | |
792 if (!supported_formats) | |
793 return false; | |
794 | |
795 bool found_supported_format = false; | |
796 for (uint32_t index = 0; index < format_count; index++) { | |
797 if (supported_formats[index] == PP_PRINTOUTPUTFORMAT_PDF) { | |
798 // If we found PDF, we are done. | |
799 found_supported_format = true; | |
800 *format = PP_PRINTOUTPUTFORMAT_PDF; | |
801 break; | |
802 } else if (supported_formats[index] == PP_PRINTOUTPUTFORMAT_RASTER) { | |
803 // We found raster. Keep looking. | |
804 found_supported_format = true; | |
805 *format = PP_PRINTOUTPUTFORMAT_RASTER; | |
806 } | |
807 } | |
808 PluginModule::GetCore()->MemFree(supported_formats); | |
809 return found_supported_format; | |
810 } | |
811 | |
812 bool PluginInstance::SupportsPrintInterface() { | |
813 PP_PrintOutputFormat_Dev format; | |
814 return GetPreferredPrintOutputFormat(&format); | |
815 } | |
816 | |
817 int PluginInstance::PrintBegin(const gfx::Rect& printable_area, | |
818 int printer_dpi) { | |
819 PP_PrintOutputFormat_Dev format; | |
820 if (!GetPreferredPrintOutputFormat(&format)) { | |
821 // PrintBegin should not have been called since SupportsPrintInterface | |
822 // would have returned false; | |
823 NOTREACHED(); | |
824 return 0; | |
825 } | |
826 | |
827 PP_PrintSettings_Dev print_settings; | |
828 RectToPPRect(printable_area, &print_settings.printable_area); | |
829 print_settings.dpi = printer_dpi; | |
830 print_settings.orientation = PP_PRINTORIENTATION_NORMAL; | |
831 print_settings.grayscale = PP_FALSE; | |
832 print_settings.format = format; | |
833 int num_pages = plugin_print_interface_->Begin(pp_instance(), | |
834 &print_settings); | |
835 if (!num_pages) | |
836 return 0; | |
837 current_print_settings_ = print_settings; | |
838 #if defined (OS_LINUX) | |
839 num_pages_ = num_pages; | |
840 pdf_output_done_ = false; | |
841 #endif // (OS_LINUX) | |
842 return num_pages; | |
843 } | |
844 | |
845 bool PluginInstance::PrintPage(int page_number, WebKit::WebCanvas* canvas) { | |
846 DCHECK(plugin_print_interface_); | |
847 PP_PrintPageNumberRange_Dev page_range; | |
848 #if defined(OS_LINUX) | |
849 if (current_print_settings_.format == PP_PRINTOUTPUTFORMAT_PDF) { | |
850 // On Linux we will try and output all pages as PDF in the first call to | |
851 // PrintPage. This is a temporary hack. | |
852 // TODO(sanjeevr): Remove this hack and fix this by changing the print | |
853 // interfaces for WebFrame and WebPlugin. | |
854 if (page_number != 0) | |
855 return pdf_output_done_; | |
856 page_range.first_page_number = 0; | |
857 page_range.last_page_number = num_pages_ - 1; | |
858 } | |
859 #else // defined(OS_LINUX) | |
860 page_range.first_page_number = page_range.last_page_number = page_number; | |
861 #endif // defined(OS_LINUX) | |
862 | |
863 PP_Resource print_output = | |
864 plugin_print_interface_->PrintPages(pp_instance(), &page_range, 1); | |
865 | |
866 if (!print_output) | |
867 return false; | |
868 | |
869 bool ret = false; | |
870 | |
871 if (current_print_settings_.format == PP_PRINTOUTPUTFORMAT_PDF) | |
872 ret = PrintPDFOutput(print_output, canvas); | |
873 else if (current_print_settings_.format == PP_PRINTOUTPUTFORMAT_RASTER) | |
874 ret = PrintRasterOutput(print_output, canvas); | |
875 | |
876 // Now we need to release the print output resource. | |
877 PluginModule::GetCore()->ReleaseResource(print_output); | |
878 | |
879 return ret; | |
880 } | |
881 | |
882 void PluginInstance::PrintEnd() { | |
883 DCHECK(plugin_print_interface_); | |
884 if (plugin_print_interface_) | |
885 plugin_print_interface_->End(pp_instance()); | |
886 memset(¤t_print_settings_, 0, sizeof(current_print_settings_)); | |
887 #if defined(OS_MACOSX) | |
888 last_printed_page_ = NULL; | |
889 #elif defined(OS_LINUX) | |
890 num_pages_ = 0; | |
891 pdf_output_done_ = false; | |
892 #endif // defined(OS_LINUX) | |
893 } | |
894 | |
895 bool PluginInstance::IsFullscreen() { | |
896 return fullscreen_container_ != NULL; | |
897 } | |
898 | |
899 bool PluginInstance::SetFullscreen(bool fullscreen) { | |
900 bool is_fullscreen = (fullscreen_container_ != NULL); | |
901 if (fullscreen == is_fullscreen) | |
902 return true; | |
903 VLOG(1) << "Setting fullscreen to " << (fullscreen ? "on" : "off"); | |
904 if (fullscreen) { | |
905 fullscreen_container_ = delegate_->CreateFullscreenContainer(this); | |
906 } else { | |
907 fullscreen_container_->Destroy(); | |
908 fullscreen_container_ = NULL; | |
909 // TODO(piman): currently the fullscreen container resizes the plugin to the | |
910 // fullscreen size so we need to reset the size here. Eventually it will | |
911 // transparently scale and this won't be necessary. | |
912 if (container_) { | |
913 container_->reportGeometry(); | |
914 container_->invalidate(); | |
915 } | |
916 } | |
917 return true; | |
918 } | |
919 | |
920 bool PluginInstance::NavigateToURL(const char* url, const char* target) { | |
921 if (!url || !target || !container_) | |
922 return false; | |
923 | |
924 WebDocument document = container_->element().document(); | |
925 GURL complete_url = document.completeURL(WebString::fromUTF8(url)); | |
926 // Don't try to deal with the security issues of javascript. | |
927 if (complete_url.SchemeIs("javascript")) | |
928 return false; | |
929 | |
930 WebURLRequest request(complete_url); | |
931 document.frame()->setReferrerForRequest(request, GURL()); | |
932 request.setHTTPMethod(WebString::fromUTF8("GET")); | |
933 request.setFirstPartyForCookies(document.firstPartyForCookies()); | |
934 | |
935 WebString target_str = WebString::fromUTF8(target); | |
936 container_->loadFrameRequest(request, target_str, false, NULL); | |
937 return true; | |
938 } | |
939 | |
940 bool PluginInstance::PrintPDFOutput(PP_Resource print_output, | |
941 WebKit::WebCanvas* canvas) { | |
942 scoped_refptr<Buffer> buffer(Resource::GetAs<Buffer>(print_output)); | |
943 if (!buffer.get() || !buffer->is_mapped() || !buffer->size()) { | |
944 NOTREACHED(); | |
945 return false; | |
946 } | |
947 #if defined(OS_WIN) | |
948 // For Windows, we need the PDF DLL to render the output PDF to a DC. | |
949 HMODULE pdf_module = GetModuleHandle(L"pdf.dll"); | |
950 if (!pdf_module) | |
951 return false; | |
952 RenderPDFPageToDCProc render_proc = | |
953 reinterpret_cast<RenderPDFPageToDCProc>( | |
954 GetProcAddress(pdf_module, "RenderPDFPageToDC")); | |
955 if (!render_proc) | |
956 return false; | |
957 #endif // defined(OS_WIN) | |
958 | |
959 bool ret = false; | |
960 #if defined(OS_LINUX) | |
961 // On Linux we need to get the backing PdfPsMetafile and write the bits | |
962 // directly. | |
963 cairo_t* context = canvas->beginPlatformPaint(); | |
964 printing::NativeMetafile* metafile = | |
965 printing::NativeMetafile::FromCairoContext(context); | |
966 DCHECK(metafile); | |
967 if (metafile) { | |
968 ret = metafile->SetRawData(buffer->mapped_buffer(), buffer->size()); | |
969 if (ret) | |
970 pdf_output_done_ = true; | |
971 } | |
972 canvas->endPlatformPaint(); | |
973 #elif defined(OS_MACOSX) | |
974 printing::NativeMetafile metafile; | |
975 // Create a PDF metafile and render from there into the passed in context. | |
976 if (metafile.Init(buffer->mapped_buffer(), buffer->size())) { | |
977 // Flip the transform. | |
978 CGContextSaveGState(canvas); | |
979 CGContextTranslateCTM(canvas, 0, | |
980 current_print_settings_.printable_area.size.height); | |
981 CGContextScaleCTM(canvas, 1.0, -1.0); | |
982 CGRect page_rect; | |
983 page_rect.origin.x = current_print_settings_.printable_area.point.x; | |
984 page_rect.origin.y = current_print_settings_.printable_area.point.y; | |
985 page_rect.size.width = current_print_settings_.printable_area.size.width; | |
986 page_rect.size.height = current_print_settings_.printable_area.size.height; | |
987 | |
988 ret = metafile.RenderPage(1, canvas, page_rect, true, false, true, true); | |
989 CGContextRestoreGState(canvas); | |
990 } | |
991 #elif defined(OS_WIN) | |
992 // On Windows, we now need to render the PDF to the DC that backs the | |
993 // supplied canvas. | |
994 skia::VectorPlatformDevice& device = | |
995 static_cast<skia::VectorPlatformDevice&>( | |
996 canvas->getTopPlatformDevice()); | |
997 HDC dc = device.getBitmapDC(); | |
998 gfx::Size size_in_pixels; | |
999 size_in_pixels.set_width( | |
1000 printing::ConvertUnit(current_print_settings_.printable_area.size.width, | |
1001 static_cast<int>(printing::kPointsPerInch), | |
1002 current_print_settings_.dpi)); | |
1003 size_in_pixels.set_height( | |
1004 printing::ConvertUnit(current_print_settings_.printable_area.size.height, | |
1005 static_cast<int>(printing::kPointsPerInch), | |
1006 current_print_settings_.dpi)); | |
1007 // We need to render using the actual printer DPI (rendering to a smaller | |
1008 // set of pixels leads to a blurry output). However, we need to counter the | |
1009 // scaling up that will happen in the browser. | |
1010 XFORM xform = {0}; | |
1011 xform.eM11 = xform.eM22 = static_cast<float>(printing::kPointsPerInch) / | |
1012 static_cast<float>(current_print_settings_.dpi); | |
1013 ModifyWorldTransform(dc, &xform, MWT_LEFTMULTIPLY); | |
1014 | |
1015 ret = render_proc(buffer->mapped_buffer(), buffer->size(), 0, dc, | |
1016 current_print_settings_.dpi, current_print_settings_.dpi, | |
1017 0, 0, size_in_pixels.width(), | |
1018 size_in_pixels.height(), true, false, true, true); | |
1019 #endif // defined(OS_WIN) | |
1020 | |
1021 return ret; | |
1022 } | |
1023 | |
1024 bool PluginInstance::PrintRasterOutput(PP_Resource print_output, | |
1025 WebKit::WebCanvas* canvas) { | |
1026 scoped_refptr<ImageData> image(Resource::GetAs<ImageData>(print_output)); | |
1027 if (!image.get() || !image->is_mapped()) | |
1028 return false; | |
1029 | |
1030 const SkBitmap* bitmap = image->GetMappedBitmap(); | |
1031 if (!bitmap) | |
1032 return false; | |
1033 | |
1034 // Draw the printed image into the supplied canvas. | |
1035 SkIRect src_rect; | |
1036 src_rect.set(0, 0, bitmap->width(), bitmap->height()); | |
1037 SkRect dest_rect; | |
1038 dest_rect.set( | |
1039 SkIntToScalar(current_print_settings_.printable_area.point.x), | |
1040 SkIntToScalar(current_print_settings_.printable_area.point.y), | |
1041 SkIntToScalar(current_print_settings_.printable_area.point.x + | |
1042 current_print_settings_.printable_area.size.width), | |
1043 SkIntToScalar(current_print_settings_.printable_area.point.y + | |
1044 current_print_settings_.printable_area.size.height)); | |
1045 bool draw_to_canvas = true; | |
1046 gfx::Rect dest_rect_gfx; | |
1047 dest_rect_gfx.set_x(current_print_settings_.printable_area.point.x); | |
1048 dest_rect_gfx.set_y(current_print_settings_.printable_area.point.y); | |
1049 dest_rect_gfx.set_width(current_print_settings_.printable_area.size.width); | |
1050 dest_rect_gfx.set_height(current_print_settings_.printable_area.size.height); | |
1051 | |
1052 #if defined(OS_WIN) | |
1053 // Since this is a raster output, the size of the bitmap can be | |
1054 // huge (especially at high printer DPIs). On Windows, this can | |
1055 // result in a HUGE EMF (on Mac and Linux the output goes to PDF | |
1056 // which appears to Flate compress the bitmap). So, if this bitmap | |
1057 // is larger than 20 MB, we save the bitmap as a JPEG into the EMF | |
1058 // DC. Note: We chose JPEG over PNG because JPEG compression seems | |
1059 // way faster (about 4 times faster). | |
1060 static const int kCompressionThreshold = 20 * 1024 * 1024; | |
1061 if (bitmap->getSize() > kCompressionThreshold) { | |
1062 DrawJPEGToPlatformDC(*bitmap, dest_rect_gfx, canvas); | |
1063 draw_to_canvas = false; | |
1064 } | |
1065 #endif // defined(OS_WIN) | |
1066 #if defined(OS_MACOSX) | |
1067 draw_to_canvas = false; | |
1068 DrawSkBitmapToCanvas(*bitmap, canvas, dest_rect_gfx, | |
1069 current_print_settings_.printable_area.size.height); | |
1070 // See comments in the header file. | |
1071 last_printed_page_ = image; | |
1072 #else // defined(OS_MACOSX) | |
1073 if (draw_to_canvas) | |
1074 canvas->drawBitmapRect(*bitmap, &src_rect, dest_rect); | |
1075 #endif // defined(OS_MACOSX) | |
1076 return true; | |
1077 } | |
1078 | |
1079 #if defined(OS_WIN) | |
1080 bool PluginInstance::DrawJPEGToPlatformDC( | |
1081 const SkBitmap& bitmap, | |
1082 const gfx::Rect& printable_area, | |
1083 WebKit::WebCanvas* canvas) { | |
1084 skia::VectorPlatformDevice& device = | |
1085 static_cast<skia::VectorPlatformDevice&>( | |
1086 canvas->getTopPlatformDevice()); | |
1087 HDC dc = device.getBitmapDC(); | |
1088 // TODO(sanjeevr): This is a temporary hack. If we output a JPEG | |
1089 // to the EMF, the EnumEnhMetaFile call fails in the browser | |
1090 // process. The failure also happens if we output nothing here. | |
1091 // We need to investigate the reason for this failure and fix it. | |
1092 // In the meantime this temporary hack of drawing an empty | |
1093 // rectangle in the DC gets us by. | |
1094 Rectangle(dc, 0, 0, 0, 0); | |
1095 | |
1096 // Ideally we should add JPEG compression to the VectorPlatformDevice class | |
1097 // However, Skia currently has no JPEG compression code and we cannot | |
1098 // depend on gfx/jpeg_codec.h in Skia. So we do the compression here. | |
1099 SkAutoLockPixels lock(bitmap); | |
1100 DCHECK(bitmap.getConfig() == SkBitmap::kARGB_8888_Config); | |
1101 const uint32_t* pixels = | |
1102 static_cast<const uint32_t*>(bitmap.getPixels()); | |
1103 std::vector<unsigned char> compressed_image; | |
1104 base::TimeTicks start_time = base::TimeTicks::Now(); | |
1105 bool encoded = gfx::JPEGCodec::Encode( | |
1106 reinterpret_cast<const unsigned char*>(pixels), | |
1107 gfx::JPEGCodec::FORMAT_BGRA, bitmap.width(), bitmap.height(), | |
1108 static_cast<int>(bitmap.rowBytes()), 100, &compressed_image); | |
1109 UMA_HISTOGRAM_TIMES("PepperPluginPrint.RasterBitmapCompressTime", | |
1110 base::TimeTicks::Now() - start_time); | |
1111 if (!encoded) { | |
1112 NOTREACHED(); | |
1113 return false; | |
1114 } | |
1115 BITMAPINFOHEADER bmi = {0}; | |
1116 gfx::CreateBitmapHeader(bitmap.width(), bitmap.height(), &bmi); | |
1117 bmi.biCompression = BI_JPEG; | |
1118 bmi.biSizeImage = compressed_image.size(); | |
1119 bmi.biHeight = -bmi.biHeight; | |
1120 StretchDIBits(dc, printable_area.x(), printable_area.y(), | |
1121 printable_area.width(), printable_area.height(), | |
1122 0, 0, bitmap.width(), bitmap.height(), | |
1123 &compressed_image.front(), | |
1124 reinterpret_cast<const BITMAPINFO*>(&bmi), | |
1125 DIB_RGB_COLORS, SRCCOPY); | |
1126 return true; | |
1127 } | |
1128 #endif // OS_WIN | |
1129 | |
1130 #if defined(OS_MACOSX) | |
1131 void PluginInstance::DrawSkBitmapToCanvas( | |
1132 const SkBitmap& bitmap, WebKit::WebCanvas* canvas, | |
1133 const gfx::Rect& dest_rect, | |
1134 int canvas_height) { | |
1135 SkAutoLockPixels lock(bitmap); | |
1136 DCHECK(bitmap.getConfig() == SkBitmap::kARGB_8888_Config); | |
1137 base::mac::ScopedCFTypeRef<CGDataProviderRef> data_provider( | |
1138 CGDataProviderCreateWithData( | |
1139 NULL, bitmap.getAddr32(0, 0), | |
1140 bitmap.rowBytes() * bitmap.height(), NULL)); | |
1141 base::mac::ScopedCFTypeRef<CGImageRef> image( | |
1142 CGImageCreate( | |
1143 bitmap.width(), bitmap.height(), | |
1144 8, 32, bitmap.rowBytes(), | |
1145 mac_util::GetSystemColorSpace(), | |
1146 kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host, | |
1147 data_provider, NULL, false, kCGRenderingIntentDefault)); | |
1148 | |
1149 // Flip the transform | |
1150 CGContextSaveGState(canvas); | |
1151 CGContextTranslateCTM(canvas, 0, canvas_height); | |
1152 CGContextScaleCTM(canvas, 1.0, -1.0); | |
1153 | |
1154 CGRect bounds; | |
1155 bounds.origin.x = dest_rect.x(); | |
1156 bounds.origin.y = canvas_height - dest_rect.y() - dest_rect.height(); | |
1157 bounds.size.width = dest_rect.width(); | |
1158 bounds.size.height = dest_rect.height(); | |
1159 | |
1160 CGContextDrawImage(canvas, bounds, image); | |
1161 CGContextRestoreGState(canvas); | |
1162 } | |
1163 #endif // defined(OS_MACOSX) | |
1164 | |
1165 Graphics2D* PluginInstance::bound_graphics_2d() const { | |
1166 if (bound_graphics_.get() == NULL) | |
1167 return NULL; | |
1168 | |
1169 return bound_graphics_->Cast<Graphics2D>(); | |
1170 } | |
1171 | |
1172 Graphics3D* PluginInstance::bound_graphics_3d() const { | |
1173 if (bound_graphics_.get() == NULL) | |
1174 return NULL; | |
1175 | |
1176 return bound_graphics_->Cast<Graphics3D>(); | |
1177 } | |
1178 | |
1179 } // namespace pepper | |
OLD | NEW |