| // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #define PEPPER_APIS_ENABLED 1 |
| |
| #include "chrome/renderer/webplugin_delegate_pepper.h" |
| |
| #include <string> |
| #include <vector> |
| |
| #if defined(OS_LINUX) |
| #include <unistd.h> |
| #endif |
| |
| #include "base/file_path.h" |
| #include "base/file_util.h" |
| #if defined(OS_MACOSX) |
| #include "base/mac_util.h" |
| #endif |
| #include "base/md5.h" |
| #include "base/message_loop.h" |
| #include "base/path_service.h" |
| #include "base/process_util.h" |
| #if defined(OS_MACOSX) |
| #include "base/scoped_cftyperef.h" |
| #endif |
| #include "base/scoped_ptr.h" |
| #include "base/stats_counters.h" |
| #include "base/string_number_conversions.h" |
| #include "base/string_util.h" |
| #include "base/task.h" |
| #include "base/time.h" |
| #include "chrome/common/chrome_paths.h" |
| #include "chrome/common/render_messages.h" |
| #include "chrome/renderer/pepper_widget.h" |
| #include "chrome/renderer/render_thread.h" |
| #include "chrome/renderer/render_view.h" |
| #if defined(OS_LINUX) |
| #include "chrome/renderer/renderer_sandbox_support_linux.h" |
| #endif |
| #include "chrome/renderer/webplugin_delegate_proxy.h" |
| #include "gfx/blit.h" |
| #if defined(OS_WIN) |
| #include "gfx/codec/jpeg_codec.h" |
| #include "gfx/gdi_util.h" |
| #include "printing/units.h" |
| #include "skia/ext/vector_platform_device.h" |
| #endif |
| #include "printing/native_metafile.h" |
| #include "third_party/npapi/bindings/npapi_extensions.h" |
| #include "third_party/npapi/bindings/npapi_extensions_private.h" |
| #include "third_party/WebKit/WebKit/chromium/public/WebCursorInfo.h" |
| #include "third_party/WebKit/WebKit/chromium/public/WebInputEvent.h" |
| #include "webkit/glue/plugins/plugin_constants_win.h" |
| #include "webkit/glue/plugins/plugin_instance.h" |
| #include "webkit/glue/plugins/plugin_lib.h" |
| #include "webkit/glue/plugins/plugin_list.h" |
| #include "webkit/glue/plugins/plugin_host.h" |
| #include "webkit/glue/plugins/plugin_stream_url.h" |
| #include "webkit/glue/webcursor.h" |
| #include "webkit/glue/webkit_glue.h" |
| |
| #if defined(ENABLE_GPU) |
| #include "webkit/glue/plugins/plugin_constants_win.h" |
| #endif |
| |
| #if defined(ENABLE_GPU) |
| using gpu::Buffer; |
| #endif |
| |
| using webkit_glue::WebPlugin; |
| using webkit_glue::WebPluginDelegate; |
| using webkit_glue::WebPluginResourceClient; |
| using WebKit::WebCursorInfo; |
| using WebKit::WebKeyboardEvent; |
| using WebKit::WebInputEvent; |
| using WebKit::WebMouseEvent; |
| using WebKit::WebMouseWheelEvent; |
| |
| namespace { |
| |
| // Implementation artifacts for a context |
| struct Device2DImpl { |
| TransportDIB* dib; |
| }; |
| |
| struct Device3DImpl { |
| #if defined(ENABLE_GPU) |
| gpu::CommandBuffer* command_buffer; |
| #endif |
| bool dynamically_created; |
| }; |
| |
| const int32 kDefaultCommandBufferSize = 1024 * 1024; |
| |
| } // namespace |
| |
| static const float kPointsPerInch = 72.0; |
| |
| #if defined(OS_WIN) |
| // Exported by pdf.dll |
| typedef bool (*RenderPDFPageToDCProc)( |
| const unsigned char* pdf_buffer, int buffer_size, int page_number, HDC dc, |
| int dpi_x, int dpi_y, int bounds_origin_x, int bounds_origin_y, |
| int bounds_width, int bounds_height, bool fit_to_bounds, |
| bool stretch_to_bounds, bool keep_aspect_ratio, bool center_in_bounds); |
| #endif // defined(OS_WIN) |
| |
| WebPluginDelegatePepper* WebPluginDelegatePepper::Create( |
| const FilePath& filename, |
| const std::string& mime_type, |
| const base::WeakPtr<RenderView>& render_view) { |
| scoped_refptr<NPAPI::PluginLib> plugin_lib = |
| NPAPI::PluginLib::CreatePluginLib(filename); |
| if (plugin_lib.get() == NULL) |
| return NULL; |
| |
| NPError err = plugin_lib->NP_Initialize(); |
| if (err != NPERR_NO_ERROR) |
| return NULL; |
| |
| scoped_refptr<NPAPI::PluginInstance> instance = |
| plugin_lib->CreateInstance(mime_type); |
| return new WebPluginDelegatePepper(render_view, |
| instance.get()); |
| } |
| |
| void WebPluginDelegatePepper::didChooseFile( |
| const WebKit::WebVector<WebKit::WebString>& file_names) { |
| if (file_names.isEmpty()) { |
| current_choose_file_callback_(NULL, 0, current_choose_file_user_data_); |
| } else { |
| // Construct a bunch of 8-bit strings for the callback. |
| std::vector<std::string> file_strings; |
| file_strings.resize(file_names.size()); |
| for (size_t i = 0; i < file_names.size(); i++) |
| file_strings[i] = file_names[0].utf8(); |
| |
| // Construct an array of pointers to each of the strings. |
| std::vector<const char*> pointers_to_strings; |
| pointers_to_strings.resize(file_strings.size()); |
| for (size_t i = 0; i < file_strings.size(); i++) |
| pointers_to_strings[i] = file_strings[i].c_str(); |
| |
| current_choose_file_callback_( |
| &pointers_to_strings[0], |
| static_cast<int>(pointers_to_strings.size()), |
| current_choose_file_user_data_); |
| } |
| } |
| |
| bool WebPluginDelegatePepper::Initialize( |
| const GURL& url, |
| const std::vector<std::string>& arg_names, |
| const std::vector<std::string>& arg_values, |
| WebPlugin* plugin, |
| bool load_manually) { |
| plugin_ = plugin; |
| |
| instance_->set_web_plugin(plugin_); |
| int argc = 0; |
| scoped_array<char*> argn(new char*[arg_names.size()]); |
| scoped_array<char*> argv(new char*[arg_names.size()]); |
| for (size_t i = 0; i < arg_names.size(); ++i) { |
| argn[argc] = const_cast<char*>(arg_names[i].c_str()); |
| argv[argc] = const_cast<char*>(arg_values[i].c_str()); |
| argc++; |
| } |
| |
| bool start_result = instance_->Start( |
| url, argn.get(), argv.get(), argc, load_manually); |
| if (!start_result) |
| return false; |
| |
| plugin_url_ = url.spec(); |
| |
| return true; |
| } |
| |
| void WebPluginDelegatePepper::DestroyInstance() { |
| if (instance_ && (instance_->npp()->ndata != NULL)) { |
| // Shutdown all streams before destroying so that |
| // no streams are left "in progress". Need to do |
| // this before calling set_web_plugin(NULL) because the |
| // instance uses the helper to do the download. |
| instance_->CloseStreams(); |
| |
| window_.window = NULL; |
| instance_->NPP_SetWindow(&window_); |
| |
| instance_->NPP_Destroy(); |
| |
| instance_->set_web_plugin(NULL); |
| |
| instance_ = 0; |
| } |
| |
| // Destroy the nested GPU plugin only after first destroying the underlying |
| // Pepper plugin. This is so the Pepper plugin does not attempt to issue |
| // rendering commands after the GPU plugin has stopped processing them and |
| // responding to them. |
| if (nested_delegate_) { |
| #if defined(ENABLE_GPU) |
| if (command_buffer_) { |
| nested_delegate_->DestroyCommandBuffer(command_buffer_); |
| command_buffer_ = NULL; |
| } |
| #endif |
| |
| nested_delegate_->PluginDestroyed(); |
| nested_delegate_ = NULL; |
| } |
| } |
| |
| void WebPluginDelegatePepper::UpdateGeometry( |
| const gfx::Rect& window_rect, |
| const gfx::Rect& clip_rect) { |
| // Only resend to the instance if the geometry has changed. |
| if (window_rect == window_rect_ && clip_rect == clip_rect_) |
| return; |
| |
| clip_rect_ = clip_rect; |
| cutout_rects_.clear(); |
| |
| if (window_rect_ == window_rect) |
| return; |
| window_rect_ = window_rect; |
| |
| // TODO(brettw) figure out how to tell the plugin that the size changed and it |
| // needs to repaint? |
| SkBitmap new_committed; |
| new_committed.setConfig(SkBitmap::kARGB_8888_Config, |
| window_rect_.width(), window_rect_.height()); |
| new_committed.allocPixels(); |
| committed_bitmap_ = new_committed; |
| |
| // Forward the new geometry to the nested plugin instance. |
| if (nested_delegate_) |
| nested_delegate_->UpdateGeometry(window_rect, clip_rect); |
| |
| #if defined(ENABLE_GPU) |
| #if defined(OS_MACOSX) |
| // Send the new window size to the command buffer service code so it |
| // can allocate a new backing store. The handle to the new backing |
| // store is sent back to the browser asynchronously. |
| if (command_buffer_) { |
| command_buffer_->SetWindowSize(window_rect_.size()); |
| } |
| #endif // OS_MACOSX |
| #endif // ENABLE_GPU |
| |
| if (!instance()) |
| return; |
| |
| ForwardSetWindow(); |
| } |
| |
| NPObject* WebPluginDelegatePepper::GetPluginScriptableObject() { |
| return instance_->GetPluginScriptableObject(); |
| } |
| |
| void WebPluginDelegatePepper::DidFinishLoadWithReason( |
| const GURL& url, NPReason reason, int notify_id) { |
| instance()->DidFinishLoadWithReason(url, reason, notify_id); |
| } |
| |
| int WebPluginDelegatePepper::GetProcessId() { |
| // We are in process, so the plugin pid is this current process pid. |
| return base::GetCurrentProcId(); |
| } |
| |
| void WebPluginDelegatePepper::SendJavaScriptStream( |
| const GURL& url, |
| const std::string& result, |
| bool success, |
| int notify_id) { |
| instance()->SendJavaScriptStream(url, result, success, notify_id); |
| } |
| |
| void WebPluginDelegatePepper::DidReceiveManualResponse( |
| const GURL& url, const std::string& mime_type, |
| const std::string& headers, uint32 expected_length, uint32 last_modified) { |
| instance()->DidReceiveManualResponse(url, mime_type, headers, |
| expected_length, last_modified); |
| } |
| |
| void WebPluginDelegatePepper::DidReceiveManualData(const char* buffer, |
| int length) { |
| instance()->DidReceiveManualData(buffer, length); |
| } |
| |
| void WebPluginDelegatePepper::DidFinishManualLoading() { |
| instance()->DidFinishManualLoading(); |
| } |
| |
| void WebPluginDelegatePepper::DidManualLoadFail() { |
| instance()->DidManualLoadFail(); |
| } |
| |
| FilePath WebPluginDelegatePepper::GetPluginPath() { |
| return instance()->plugin_lib()->plugin_info().path; |
| } |
| |
| void WebPluginDelegatePepper::RenderViewInitiatedPaint() { |
| // Broadcast event to all 2D contexts. |
| Graphics2DMap::iterator iter2d(&graphic2d_contexts_); |
| while (!iter2d.IsAtEnd()) { |
| iter2d.GetCurrentValue()->RenderViewInitiatedPaint(); |
| iter2d.Advance(); |
| } |
| } |
| |
| void WebPluginDelegatePepper::RenderViewFlushedPaint() { |
| // Broadcast event to all 2D contexts. |
| Graphics2DMap::iterator iter2d(&graphic2d_contexts_); |
| while (!iter2d.IsAtEnd()) { |
| iter2d.GetCurrentValue()->RenderViewFlushedPaint(); |
| iter2d.Advance(); |
| } |
| } |
| |
| WebPluginResourceClient* WebPluginDelegatePepper::CreateResourceClient( |
| unsigned long resource_id, const GURL& url, int notify_id) { |
| return instance()->CreateStream(resource_id, url, std::string(), notify_id); |
| } |
| |
| WebPluginResourceClient* WebPluginDelegatePepper::CreateSeekableResourceClient( |
| unsigned long resource_id, int range_request_id) { |
| return instance()->GetRangeRequest(range_request_id); |
| } |
| |
| bool WebPluginDelegatePepper::StartFind(const string16& search_text, |
| bool case_sensitive, |
| int identifier) { |
| if (!GetFindExtensions()) |
| return false; |
| find_identifier_ = identifier; |
| GetFindExtensions()->startFind( |
| instance()->npp(), UTF16ToUTF8(search_text).c_str(), case_sensitive); |
| return true; |
| } |
| |
| void WebPluginDelegatePepper::SelectFindResult(bool forward) { |
| GetFindExtensions()->selectFindResult(instance()->npp(), forward); |
| } |
| |
| void WebPluginDelegatePepper::StopFind() { |
| find_identifier_ = -1; |
| GetFindExtensions()->stopFind(instance()->npp()); |
| } |
| |
| void WebPluginDelegatePepper::NumberOfFindResultsChanged(int total, |
| bool final_result) { |
| DCHECK(find_identifier_ != -1); |
| |
| render_view_->reportFindInPageMatchCount( |
| find_identifier_, total, final_result); |
| } |
| |
| void WebPluginDelegatePepper::SelectedFindResultChanged(int index) { |
| render_view_->reportFindInPageSelection( |
| find_identifier_, index + 1, WebKit::WebRect()); |
| } |
| |
| bool WebPluginDelegatePepper::ChooseFile(const char* mime_types, |
| int mode, |
| NPChooseFileCallback callback, |
| void* user_data) { |
| if (!render_view_ || !callback) |
| return false; |
| |
| if (current_choose_file_callback_) |
| return false; // Reentrant call to browse, only one can be outstanding |
| // per plugin. |
| |
| // TODO(brettw) do something with the mime types! |
| current_choose_file_callback_ = callback; |
| current_choose_file_user_data_ = user_data; |
| |
| ViewHostMsg_RunFileChooser_Params ipc_params; |
| switch (mode) { |
| case NPChooseFile_Open: |
| ipc_params.mode = ViewHostMsg_RunFileChooser_Params::Open; |
| break; |
| case NPChooseFile_OpenMultiple: |
| ipc_params.mode = ViewHostMsg_RunFileChooser_Params::OpenMultiple; |
| break; |
| case NPChooseFile_Save: |
| ipc_params.mode = ViewHostMsg_RunFileChooser_Params::Save; |
| break; |
| default: |
| return false; |
| } |
| return render_view_->ScheduleFileChooser(ipc_params, this); |
| } |
| |
| NPWidgetExtensions* WebPluginDelegatePepper::GetWidgetExtensions() { |
| return PepperWidget::GetWidgetExtensions(); |
| } |
| |
| #define COMPILE_ASSERT_MATCHING_ENUM(webkit_name, np_name) \ |
| COMPILE_ASSERT(int(WebCursorInfo::webkit_name) == int(np_name), \ |
| mismatching_enums) |
| |
| COMPILE_ASSERT_MATCHING_ENUM(TypePointer, NPCursorTypePointer); |
| COMPILE_ASSERT_MATCHING_ENUM(TypeCross, NPCursorTypeCross); |
| COMPILE_ASSERT_MATCHING_ENUM(TypeHand, NPCursorTypeHand); |
| COMPILE_ASSERT_MATCHING_ENUM(TypeIBeam, NPCursorTypeIBeam); |
| COMPILE_ASSERT_MATCHING_ENUM(TypeWait, NPCursorTypeWait); |
| COMPILE_ASSERT_MATCHING_ENUM(TypeHelp, NPCursorTypeHelp); |
| COMPILE_ASSERT_MATCHING_ENUM(TypeEastResize, NPCursorTypeEastResize); |
| COMPILE_ASSERT_MATCHING_ENUM(TypeNorthResize, NPCursorTypeNorthResize); |
| COMPILE_ASSERT_MATCHING_ENUM(TypeNorthEastResize, NPCursorTypeNorthEastResize); |
| COMPILE_ASSERT_MATCHING_ENUM(TypeNorthWestResize, NPCursorTypeNorthWestResize); |
| COMPILE_ASSERT_MATCHING_ENUM(TypeSouthResize, NPCursorTypeSouthResize); |
| COMPILE_ASSERT_MATCHING_ENUM(TypeSouthEastResize, NPCursorTypeSouthEastResize); |
| COMPILE_ASSERT_MATCHING_ENUM(TypeSouthWestResize, NPCursorTypeSouthWestResize); |
| COMPILE_ASSERT_MATCHING_ENUM(TypeWestResize, NPCursorTypeWestResize); |
| COMPILE_ASSERT_MATCHING_ENUM(TypeNorthSouthResize, |
| NPCursorTypeNorthSouthResize); |
| COMPILE_ASSERT_MATCHING_ENUM(TypeEastWestResize, NPCursorTypeEastWestResize); |
| COMPILE_ASSERT_MATCHING_ENUM(TypeNorthEastSouthWestResize, |
| NPCursorTypeNorthEastSouthWestResize); |
| COMPILE_ASSERT_MATCHING_ENUM(TypeNorthWestSouthEastResize, |
| NPCursorTypeNorthWestSouthEastResize); |
| COMPILE_ASSERT_MATCHING_ENUM(TypeColumnResize, NPCursorTypeColumnResize); |
| COMPILE_ASSERT_MATCHING_ENUM(TypeRowResize, NPCursorTypeRowResize); |
| COMPILE_ASSERT_MATCHING_ENUM(TypeMiddlePanning, NPCursorTypeMiddlePanning); |
| COMPILE_ASSERT_MATCHING_ENUM(TypeEastPanning, NPCursorTypeEastPanning); |
| COMPILE_ASSERT_MATCHING_ENUM(TypeNorthPanning, NPCursorTypeNorthPanning); |
| COMPILE_ASSERT_MATCHING_ENUM(TypeNorthEastPanning, |
| NPCursorTypeNorthEastPanning); |
| COMPILE_ASSERT_MATCHING_ENUM(TypeNorthWestPanning, |
| NPCursorTypeNorthWestPanning); |
| COMPILE_ASSERT_MATCHING_ENUM(TypeSouthPanning, NPCursorTypeSouthPanning); |
| COMPILE_ASSERT_MATCHING_ENUM(TypeSouthEastPanning, |
| NPCursorTypeSouthEastPanning); |
| COMPILE_ASSERT_MATCHING_ENUM(TypeSouthWestPanning, |
| NPCursorTypeSouthWestPanning); |
| COMPILE_ASSERT_MATCHING_ENUM(TypeWestPanning, NPCursorTypeWestPanning); |
| COMPILE_ASSERT_MATCHING_ENUM(TypeMove, NPCursorTypeMove); |
| COMPILE_ASSERT_MATCHING_ENUM(TypeVerticalText, NPCursorTypeVerticalText); |
| COMPILE_ASSERT_MATCHING_ENUM(TypeCell, NPCursorTypeCell); |
| COMPILE_ASSERT_MATCHING_ENUM(TypeContextMenu, NPCursorTypeContextMenu); |
| COMPILE_ASSERT_MATCHING_ENUM(TypeAlias, NPCursorTypeAlias); |
| COMPILE_ASSERT_MATCHING_ENUM(TypeProgress, NPCursorTypeProgress); |
| COMPILE_ASSERT_MATCHING_ENUM(TypeNoDrop, NPCursorTypeNoDrop); |
| COMPILE_ASSERT_MATCHING_ENUM(TypeCopy, NPCursorTypeCopy); |
| COMPILE_ASSERT_MATCHING_ENUM(TypeNone, NPCursorTypeNone); |
| COMPILE_ASSERT_MATCHING_ENUM(TypeNotAllowed, NPCursorTypeNotAllowed); |
| COMPILE_ASSERT_MATCHING_ENUM(TypeZoomIn, NPCursorTypeZoomIn); |
| COMPILE_ASSERT_MATCHING_ENUM(TypeZoomOut, NPCursorTypeZoomOut); |
| |
| bool WebPluginDelegatePepper::SetCursor(NPCursorType type) { |
| cursor_.reset(new WebCursorInfo(static_cast<WebCursorInfo::Type>(type))); |
| return true; |
| } |
| |
| NPError NPMatchFontWithFallback(NPP instance, |
| const NPFontDescription* description, |
| NPFontID* id) { |
| #if defined(OS_LINUX) |
| int fd = renderer_sandbox_support::MatchFontWithFallback( |
| description->face, description->weight >= 700, description->italic, |
| description->charset); |
| if (fd == -1) |
| return NPERR_GENERIC_ERROR; |
| *id = fd; |
| return NPERR_NO_ERROR; |
| #else |
| NOTIMPLEMENTED(); |
| return NPERR_GENERIC_ERROR; |
| #endif |
| } |
| |
| NPError GetFontTable(NPP instance, |
| NPFontID id, |
| uint32_t table, |
| void* output, |
| size_t* output_length) { |
| #if defined(OS_LINUX) |
| bool rv = renderer_sandbox_support::GetFontTable( |
| id, table, static_cast<uint8_t*>(output), output_length); |
| return rv ? NPERR_NO_ERROR : NPERR_GENERIC_ERROR; |
| #else |
| NOTIMPLEMENTED(); |
| return NPERR_GENERIC_ERROR; |
| #endif |
| } |
| |
| NPError NPDestroyFont(NPP instance, NPFontID id) { |
| #if defined(OS_LINUX) |
| close(id); |
| return NPERR_NO_ERROR; |
| #else |
| NOTIMPLEMENTED(); |
| return NPERR_GENERIC_ERROR; |
| #endif |
| } |
| |
| NPFontExtensions g_font_extensions = { |
| NPMatchFontWithFallback, |
| GetFontTable, |
| NPDestroyFont |
| }; |
| |
| NPFontExtensions* WebPluginDelegatePepper::GetFontExtensions() { |
| return &g_font_extensions; |
| } |
| |
| void WebPluginDelegatePepper::SetZoomFactor(float scale, bool text_only) { |
| NPPExtensions* extensions = NULL; |
| instance()->NPP_GetValue(NPPVPepperExtensions, &extensions); |
| if (extensions && extensions->zoom) |
| extensions->zoom(instance()->npp(), scale, text_only); |
| } |
| |
| bool WebPluginDelegatePepper::HasSelection() const { |
| return !GetSelectedText(false).empty(); |
| } |
| |
| string16 WebPluginDelegatePepper::GetSelectionAsText() const { |
| return GetSelectedText(false); |
| } |
| |
| string16 WebPluginDelegatePepper::GetSelectionAsMarkup() const { |
| return GetSelectedText(true); |
| } |
| |
| string16 WebPluginDelegatePepper::GetSelectedText(bool html) const { |
| NPPExtensions* extensions = NULL; |
| instance_->NPP_GetValue(NPPVPepperExtensions, &extensions); |
| if (!extensions || !extensions->getSelection) |
| return string16(); |
| |
| void* text; |
| NPSelectionType type = html ? NPSelectionTypeHTML : NPSelectionTypePlainText; |
| NPP npp = instance_->npp(); |
| if (extensions->getSelection(npp, &type, &text) != NPERR_NO_ERROR) |
| return string16(); |
| |
| string16 rv = UTF8ToUTF16(static_cast<char*>(text)); |
| NPAPI::PluginHost::Singleton()->host_functions()->memfree(text); |
| return rv; |
| } |
| |
| NPError WebPluginDelegatePepper::Device2DQueryCapability(int32 capability, |
| int32* value) { |
| return NPERR_GENERIC_ERROR; |
| } |
| |
| NPError WebPluginDelegatePepper::Device2DQueryConfig( |
| const NPDeviceContext2DConfig* request, |
| NPDeviceContext2DConfig* obtain) { |
| return NPERR_GENERIC_ERROR; |
| } |
| |
| NPError WebPluginDelegatePepper::Device2DInitializeContext( |
| const NPDeviceContext2DConfig* config, |
| NPDeviceContext2D* context) { |
| |
| if (!render_view_) { |
| return NPERR_GENERIC_ERROR; |
| } |
| |
| // This is a windowless plugin, so set it to have no handle. Defer this |
| // until we know the plugin will use the 2D device. If it uses the 3D device |
| // it will have a window handle. |
| plugin_->SetWindow(gfx::kNullPluginWindow); |
| |
| scoped_ptr<Graphics2DDeviceContext> g2d(new Graphics2DDeviceContext(this)); |
| NPError status = g2d->Initialize(window_rect_, config, context); |
| if (NPERR_NO_ERROR == status) { |
| context->reserved = reinterpret_cast<void *>( |
| graphic2d_contexts_.Add(g2d.release())); |
| } |
| return status; |
| } |
| |
| NPError WebPluginDelegatePepper::Device2DSetStateContext( |
| NPDeviceContext2D* context, |
| int32 state, |
| intptr_t value) { |
| return NPERR_GENERIC_ERROR; |
| } |
| |
| NPError WebPluginDelegatePepper::Device2DGetStateContext( |
| NPDeviceContext2D* context, |
| int32 state, |
| intptr_t* value) { |
| if (state == NPExtensionsReservedStateSharedMemory) { |
| if (!context) |
| return NPERR_INVALID_PARAM; |
| Graphics2DDeviceContext* ctx = GetGraphicsContext(context); |
| if (!ctx) |
| return NPERR_INVALID_PARAM; |
| *value = reinterpret_cast<intptr_t>(ctx->transport_dib()); |
| return NPERR_NO_ERROR; |
| } else if (state == NPExtensionsReservedStateSharedMemoryChecksum) { |
| if (!context) |
| return NPERR_INVALID_PARAM; |
| // Bytes per pixel. |
| static const int kBytesPixel = 4; |
| int32 row_count = context->dirty.bottom - context->dirty.top; |
| int32 stride = context->dirty.right - context->dirty.left; |
| size_t length = row_count * stride * kBytesPixel; |
| MD5Digest md5_result; // 128-bit digest |
| MD5Sum(context->region, length, &md5_result); |
| std::string hex_md5 = MD5DigestToBase16(md5_result); |
| // Return the least significant 8 characters (i.e. 4 bytes) |
| // of the 32 character hexadecimal result as an int. |
| int int_val; |
| base::HexStringToInt(hex_md5.substr(24), &int_val); |
| *value = int_val; |
| return NPERR_NO_ERROR; |
| } |
| return NPERR_GENERIC_ERROR; |
| } |
| |
| NPError WebPluginDelegatePepper::Device2DFlushContext( |
| NPP id, |
| NPDeviceContext2D* context, |
| NPDeviceFlushContextCallbackPtr callback, |
| void* user_data) { |
| if (!context) |
| return NPERR_INVALID_PARAM; |
| |
| Graphics2DDeviceContext* ctx = graphic2d_contexts_.Lookup( |
| reinterpret_cast<intptr_t>(context->reserved)); |
| if (!ctx) |
| return NPERR_INVALID_PARAM; // TODO(brettw) call callback. |
| |
| return ctx->Flush(&committed_bitmap_, context, callback, id, user_data); |
| } |
| |
| NPError WebPluginDelegatePepper::Device2DDestroyContext( |
| NPDeviceContext2D* context) { |
| |
| if (!context || !graphic2d_contexts_.Lookup( |
| reinterpret_cast<intptr_t>(context->reserved))) { |
| return NPERR_INVALID_PARAM; |
| } |
| graphic2d_contexts_.Remove(reinterpret_cast<intptr_t>(context->reserved)); |
| memset(context, 0, sizeof(NPDeviceContext2D)); |
| return NPERR_NO_ERROR; |
| } |
| |
| Graphics2DDeviceContext* WebPluginDelegatePepper::GetGraphicsContext( |
| NPDeviceContext2D* context) { |
| return graphic2d_contexts_.Lookup( |
| reinterpret_cast<intptr_t>(context->reserved)); |
| } |
| |
| NPError WebPluginDelegatePepper::Device3DQueryCapability(int32 capability, |
| int32* value) { |
| return NPERR_GENERIC_ERROR; |
| } |
| |
| NPError WebPluginDelegatePepper::Device3DQueryConfig( |
| const NPDeviceContext3DConfig* request, |
| NPDeviceContext3DConfig* obtain) { |
| return NPERR_GENERIC_ERROR; |
| } |
| |
| NPError WebPluginDelegatePepper::Device3DInitializeContext( |
| const NPDeviceContext3DConfig* config, |
| NPDeviceContext3D* context) { |
| if (!context) |
| return NPERR_GENERIC_ERROR; |
| |
| #if defined(ENABLE_GPU) |
| // Check to see if the GPU plugin is already initialized and fail if so. |
| if (nested_delegate_) |
| return NPERR_GENERIC_ERROR; |
| |
| // Create an instance of the GPU plugin that is responsible for 3D |
| // rendering. |
| nested_delegate_ = new WebPluginDelegateProxy(kGPUPluginMimeType, |
| render_view_); |
| |
| // TODO(apatrick): should the GPU plugin be attached to plugin_? |
| if (nested_delegate_->Initialize(GURL(), |
| std::vector<std::string>(), |
| std::vector<std::string>(), |
| plugin_, |
| false)) { |
| plugin_->SetAcceptsInputEvents(true); |
| |
| // Ensure the window has the correct size before initializing the |
| // command buffer. |
| nested_delegate_->UpdateGeometry(window_rect_, clip_rect_); |
| |
| // Ask the GPU plugin to create a command buffer and return a proxy. |
| command_buffer_ = nested_delegate_->CreateCommandBuffer(); |
| if (command_buffer_) { |
| // Initialize the proxy command buffer. |
| if (command_buffer_->Initialize(config->commandBufferSize)) { |
| // Get the initial command buffer state. |
| gpu::CommandBuffer::State state = command_buffer_->GetState(); |
| |
| // Initialize the 3D context. |
| context->reserved = NULL; |
| context->waitForProgress = true; |
| Buffer ring_buffer = command_buffer_->GetRingBuffer(); |
| context->commandBuffer = ring_buffer.ptr; |
| context->commandBufferSize = state.num_entries; |
| context->repaintCallback = NULL; |
| Synchronize3DContext(context, state); |
| |
| ScheduleHandleRepaint(instance_->npp(), context); |
| |
| #if defined(OS_MACOSX) |
| command_buffer_->SetWindowSize(window_rect_.size()); |
| #endif // OS_MACOSX |
| |
| // Make sure the nested delegate shows up in the right place |
| // on the page. |
| SendNestedDelegateGeometryToBrowser(window_rect_, clip_rect_); |
| |
| // Save the implementation information (the CommandBuffer). |
| Device3DImpl* impl = new Device3DImpl; |
| impl->command_buffer = command_buffer_; |
| impl->dynamically_created = false; |
| context->reserved = impl; |
| |
| return NPERR_NO_ERROR; |
| } |
| |
| nested_delegate_->DestroyCommandBuffer(command_buffer_); |
| command_buffer_ = NULL; |
| } |
| } |
| |
| nested_delegate_->PluginDestroyed(); |
| nested_delegate_ = NULL; |
| #endif // ENABLE_GPU |
| |
| return NPERR_GENERIC_ERROR; |
| } |
| |
| NPError WebPluginDelegatePepper::Device3DSetStateContext( |
| NPDeviceContext3D* context, |
| int32 state, |
| intptr_t value) { |
| return NPERR_GENERIC_ERROR; |
| } |
| |
| NPError WebPluginDelegatePepper::Device3DGetStateContext( |
| NPDeviceContext3D* context, |
| int32 state, |
| intptr_t* value) { |
| return NPERR_GENERIC_ERROR; |
| } |
| |
| NPError WebPluginDelegatePepper::Device3DFlushContext( |
| NPP id, |
| NPDeviceContext3D* context, |
| NPDeviceFlushContextCallbackPtr callback, |
| void* user_data) { |
| if (!context) |
| return NPERR_GENERIC_ERROR; |
| |
| #if defined(ENABLE_GPU) |
| gpu::CommandBuffer::State state; |
| |
| if (context->waitForProgress) { |
| if (callback) { |
| command_buffer_->AsyncFlush( |
| context->putOffset, |
| method_factory3d_.NewRunnableMethod( |
| &WebPluginDelegatePepper::Device3DUpdateState, |
| id, |
| context, |
| callback, |
| user_data)); |
| } else { |
| state = command_buffer_->Flush(context->putOffset); |
| Synchronize3DContext(context, state); |
| } |
| } else { |
| if (callback) { |
| command_buffer_->AsyncGetState( |
| method_factory3d_.NewRunnableMethod( |
| &WebPluginDelegatePepper::Device3DUpdateState, |
| id, |
| context, |
| callback, |
| user_data)); |
| } else { |
| state = command_buffer_->GetState(); |
| Synchronize3DContext(context, state); |
| } |
| } |
| #endif // ENABLE_GPU |
| return NPERR_NO_ERROR; |
| } |
| |
| NPError WebPluginDelegatePepper::Device3DDestroyContext( |
| NPDeviceContext3D* context) { |
| if (!context) |
| return NPERR_GENERIC_ERROR; |
| |
| #if defined(ENABLE_GPU) |
| // Prevent any async flush callbacks from being invoked after the context |
| // has been destroyed. |
| method_factory3d_.RevokeAll(); |
| |
| // TODO(apatrick): this will be much simpler when we switch to the new device |
| // API. There should be no need for the Device3DImpl and the context will |
| // always be destroyed dynamically. |
| Device3DImpl* impl = static_cast<Device3DImpl*>(context->reserved); |
| bool dynamically_created = impl->dynamically_created; |
| delete impl; |
| context->reserved = NULL; |
| if (dynamically_created) { |
| delete context; |
| } |
| |
| if (nested_delegate_) { |
| if (command_buffer_) { |
| nested_delegate_->DestroyCommandBuffer(command_buffer_); |
| command_buffer_ = NULL; |
| } |
| |
| nested_delegate_->PluginDestroyed(); |
| nested_delegate_ = NULL; |
| } |
| #endif // ENABLE_GPU |
| |
| return NPERR_NO_ERROR; |
| } |
| |
| NPError WebPluginDelegatePepper::Device3DCreateBuffer( |
| NPDeviceContext3D* context, |
| size_t size, |
| int32* id) { |
| if (!context) |
| return NPERR_GENERIC_ERROR; |
| |
| #if defined(ENABLE_GPU) |
| *id = command_buffer_->CreateTransferBuffer(size); |
| if (*id < 0) |
| return NPERR_GENERIC_ERROR; |
| #endif // ENABLE_GPU |
| |
| return NPERR_NO_ERROR; |
| } |
| |
| NPError WebPluginDelegatePepper::Device3DDestroyBuffer( |
| NPDeviceContext3D* context, |
| int32 id) { |
| if (!context) |
| return NPERR_GENERIC_ERROR; |
| |
| #if defined(ENABLE_GPU) |
| command_buffer_->DestroyTransferBuffer(id); |
| #endif // ENABLE_GPU |
| return NPERR_NO_ERROR; |
| } |
| |
| NPError WebPluginDelegatePepper::Device3DMapBuffer( |
| NPDeviceContext3D* context, |
| int32 id, |
| NPDeviceBuffer* np_buffer) { |
| if (!context) |
| return NPERR_GENERIC_ERROR; |
| |
| #if defined(ENABLE_GPU) |
| Buffer gpu_buffer; |
| if (id == NP3DCommandBufferId) { |
| gpu_buffer = command_buffer_->GetRingBuffer(); |
| } else { |
| gpu_buffer = command_buffer_->GetTransferBuffer(id); |
| } |
| |
| np_buffer->ptr = gpu_buffer.ptr; |
| np_buffer->size = gpu_buffer.size; |
| if (!np_buffer->ptr) |
| return NPERR_GENERIC_ERROR; |
| #endif // ENABLE_GPU |
| |
| return NPERR_NO_ERROR; |
| } |
| |
| NPError WebPluginDelegatePepper::Device3DGetNumConfigs(int32* num_configs) { |
| if (!num_configs) |
| return NPERR_GENERIC_ERROR; |
| |
| *num_configs = 1; |
| return NPERR_NO_ERROR; |
| } |
| |
| NPError WebPluginDelegatePepper::Device3DGetConfigAttribs( |
| int32 config, |
| int32* attrib_list) { |
| // Only one config available currently. |
| if (config != 0) |
| return NPERR_GENERIC_ERROR; |
| |
| if (attrib_list) { |
| for (int32* attrib_pair = attrib_list; *attrib_pair; attrib_pair += 2) { |
| switch (attrib_pair[0]) { |
| case NP3DAttrib_BufferSize: |
| attrib_pair[1] = 32; |
| break; |
| case NP3DAttrib_AlphaSize: |
| case NP3DAttrib_BlueSize: |
| case NP3DAttrib_GreenSize: |
| case NP3DAttrib_RedSize: |
| attrib_pair[1] = 8; |
| break; |
| case NP3DAttrib_DepthSize: |
| attrib_pair[1] = 24; |
| break; |
| case NP3DAttrib_StencilSize: |
| attrib_pair[1] = 8; |
| break; |
| case NP3DAttrib_SurfaceType: |
| attrib_pair[1] = 0; |
| break; |
| default: |
| return NPERR_GENERIC_ERROR; |
| } |
| } |
| } |
| |
| return NPERR_NO_ERROR; |
| } |
| |
| NPError WebPluginDelegatePepper::Device3DCreateContext( |
| int32 config, |
| const int32* attrib_list, |
| NPDeviceContext3D** context) { |
| if (!context) |
| return NPERR_GENERIC_ERROR; |
| |
| // Only one config available currently. |
| if (config != 0) |
| return NPERR_GENERIC_ERROR; |
| |
| // For now, just use the old API to initialize the context. |
| NPDeviceContext3DConfig old_config; |
| old_config.commandBufferSize = kDefaultCommandBufferSize; |
| if (attrib_list) { |
| for (const int32* attrib_pair = attrib_list; *attrib_pair; |
| attrib_pair += 2) { |
| switch (attrib_pair[0]) { |
| case NP3DAttrib_CommandBufferSize: |
| old_config.commandBufferSize = attrib_pair[1]; |
| break; |
| default: |
| return NPERR_GENERIC_ERROR; |
| } |
| } |
| } |
| |
| *context = new NPDeviceContext3D; |
| Device3DInitializeContext(&old_config, *context); |
| |
| // Flag the context as dynamically created by the browser. TODO(apatrick): |
| // take this out when all contexts are dynamically created. |
| Device3DImpl* impl = static_cast<Device3DImpl*>((*context)->reserved); |
| impl->dynamically_created = true; |
| |
| return NPERR_NO_ERROR; |
| } |
| |
| NPError WebPluginDelegatePepper::Device3DRegisterCallback( |
| NPP id, |
| NPDeviceContext3D* context, |
| int32 callback_type, |
| NPDeviceGenericCallbackPtr callback, |
| void* callback_data) { |
| if (!context) |
| return NPERR_GENERIC_ERROR; |
| |
| switch (callback_type) { |
| case NP3DCallback_Repaint: |
| context->repaintCallback = reinterpret_cast<NPDeviceContext3DRepaintPtr>( |
| callback); |
| break; |
| default: |
| return NPERR_GENERIC_ERROR; |
| } |
| |
| return NPERR_NO_ERROR; |
| } |
| |
| NPError WebPluginDelegatePepper::Device3DSynchronizeContext( |
| NPP id, |
| NPDeviceContext3D* context, |
| NPDeviceSynchronizationMode mode, |
| const int32* input_attrib_list, |
| int32* output_attrib_list, |
| NPDeviceSynchronizeContextCallbackPtr callback, |
| void* callback_data) { |
| if (!context) |
| return NPERR_GENERIC_ERROR; |
| |
| // Copy input attributes into context. |
| if (input_attrib_list) { |
| for (const int32* attrib_pair = input_attrib_list; |
| *attrib_pair; |
| attrib_pair += 2) { |
| switch (attrib_pair[0]) { |
| case NP3DAttrib_PutOffset: |
| context->putOffset = attrib_pair[1]; |
| break; |
| default: |
| return NPERR_GENERIC_ERROR; |
| } |
| } |
| } |
| |
| // Use existing flush mechanism for now. |
| if (mode != NPDeviceSynchronizationMode_Cached) { |
| context->waitForProgress = mode == NPDeviceSynchronizationMode_Flush; |
| Device3DFlushContext(id, context, callback, callback_data); |
| } |
| |
| // Copy most recent output attributes from context. |
| // To read output attributes after the completion of an asynchronous flush, |
| // invoke SynchronizeContext again with mode |
| // NPDeviceSynchronizationMode_Cached from the callback function. |
| if (output_attrib_list) { |
| for (int32* attrib_pair = output_attrib_list; |
| *attrib_pair; |
| attrib_pair += 2) { |
| switch (attrib_pair[0]) { |
| case NP3DAttrib_CommandBufferSize: |
| attrib_pair[1] = context->commandBufferSize; |
| break; |
| case NP3DAttrib_GetOffset: |
| attrib_pair[1] = context->getOffset; |
| break; |
| case NP3DAttrib_PutOffset: |
| attrib_pair[1] = context->putOffset; |
| break; |
| case NP3DAttrib_Token: |
| attrib_pair[1] = context->token; |
| break; |
| default: |
| return NPERR_GENERIC_ERROR; |
| } |
| } |
| } |
| |
| return NPERR_NO_ERROR; |
| } |
| |
| NPError WebPluginDelegatePepper::DeviceAudioQueryCapability(int32 capability, |
| int32* value) { |
| // TODO(neb,cpu) implement QueryCapability |
| return NPERR_GENERIC_ERROR; |
| } |
| |
| NPError WebPluginDelegatePepper::DeviceAudioQueryConfig( |
| const NPDeviceContextAudioConfig* request, |
| NPDeviceContextAudioConfig* obtain) { |
| // TODO(neb,cpu) implement QueryConfig |
| return NPERR_GENERIC_ERROR; |
| } |
| |
| NPError WebPluginDelegatePepper::DeviceAudioInitializeContext( |
| const NPDeviceContextAudioConfig* config, |
| NPDeviceContextAudio* context) { |
| |
| if (!render_view_) { |
| return NPERR_GENERIC_ERROR; |
| } |
| |
| scoped_ptr<AudioDeviceContext> audio(new AudioDeviceContext()); |
| NPError status = audio->Initialize(render_view_->audio_message_filter(), |
| config, context); |
| if (NPERR_NO_ERROR == status) { |
| context->reserved = |
| reinterpret_cast<void *>(audio_contexts_.Add(audio.release())); |
| } |
| return status; |
| } |
| |
| NPError WebPluginDelegatePepper::DeviceAudioSetStateContext( |
| NPDeviceContextAudio* context, |
| int32 state, |
| intptr_t value) { |
| // TODO(neb,cpu) implement SetStateContext |
| return NPERR_GENERIC_ERROR; |
| } |
| |
| NPError WebPluginDelegatePepper::DeviceAudioGetStateContext( |
| NPDeviceContextAudio* context, |
| int32 state, |
| intptr_t* value) { |
| if (state == NPExtensionsReservedStateSharedMemory) { |
| if (!context) |
| return NPERR_INVALID_PARAM; |
| AudioDeviceContext* ctx = audio_contexts_.Lookup( |
| reinterpret_cast<intptr_t>(context->reserved)); |
| if (!ctx) |
| return NPERR_INVALID_PARAM; |
| *value = reinterpret_cast<intptr_t>(ctx->shared_memory()); |
| return NPERR_NO_ERROR; |
| } else if (state == NPExtensionsReservedStateSharedMemorySize) { |
| if (!context) |
| return NPERR_INVALID_PARAM; |
| AudioDeviceContext* ctx = audio_contexts_.Lookup( |
| reinterpret_cast<intptr_t>(context->reserved)); |
| if (!ctx) |
| return NPERR_INVALID_PARAM; |
| *value = static_cast<intptr_t>(ctx->shared_memory_size()); |
| return NPERR_NO_ERROR; |
| } else if (state == NPExtensionsReservedStateSyncChannel) { |
| if (!context) |
| return NPERR_INVALID_PARAM; |
| AudioDeviceContext* ctx = audio_contexts_.Lookup( |
| reinterpret_cast<intptr_t>(context->reserved)); |
| if (!ctx) |
| return NPERR_INVALID_PARAM; |
| *value = reinterpret_cast<intptr_t>(ctx->socket()); |
| return NPERR_NO_ERROR; |
| } |
| return NPERR_GENERIC_ERROR; |
| } |
| |
| NPError WebPluginDelegatePepper::DeviceAudioFlushContext( |
| NPP id, |
| NPDeviceContextAudio* context, |
| NPDeviceFlushContextCallbackPtr callback, |
| void* user_data) { |
| // TODO(neb,cpu) implement FlushContext |
| return NPERR_GENERIC_ERROR; |
| } |
| |
| NPError WebPluginDelegatePepper::DeviceAudioDestroyContext( |
| NPDeviceContextAudio* context) { |
| if (!context || !audio_contexts_.Lookup( |
| reinterpret_cast<intptr_t>(context->reserved))) { |
| return NPERR_INVALID_PARAM; |
| } |
| audio_contexts_.Remove(reinterpret_cast<intptr_t>(context->reserved)); |
| memset(context, 0, sizeof(NPDeviceContextAudio)); |
| return NPERR_NO_ERROR; |
| } |
| |
| bool WebPluginDelegatePepper::PrintSupportsPrintExtension() { |
| return GetPrintExtensions() != NULL; |
| } |
| |
| int WebPluginDelegatePepper::PrintBegin(const gfx::Rect& printable_area, |
| int printer_dpi) { |
| int32 num_pages = 0; |
| NPPPrintExtensions* print_extensions = GetPrintExtensions(); |
| if (print_extensions) { |
| NPRect np_printable_area = {0}; |
| np_printable_area.left = printable_area.x(); |
| np_printable_area.top = printable_area.y(); |
| np_printable_area.right = np_printable_area.left + printable_area.width(); |
| np_printable_area.bottom = np_printable_area.top + printable_area.height(); |
| if (NPERR_NO_ERROR == print_extensions->printBegin(instance()->npp(), |
| &np_printable_area, |
| printer_dpi, |
| &num_pages)) { |
| current_printable_area_ = printable_area; |
| current_printer_dpi_ = printer_dpi; |
| } |
| } |
| #if defined (OS_LINUX) |
| num_pages_ = num_pages; |
| pdf_output_done_ = false; |
| #endif // (OS_LINUX) |
| return num_pages; |
| } |
| |
| bool WebPluginDelegatePepper::VectorPrintPage(int page_number, |
| WebKit::WebCanvas* canvas) { |
| NPPPrintExtensions* print_extensions = GetPrintExtensions(); |
| if (!print_extensions) |
| return false; |
| #if defined(OS_WIN) |
| // For Windows, we need the PDF DLL to render the output PDF to a DC. |
| FilePath pdf_path; |
| PathService::Get(chrome::FILE_PDF_PLUGIN, &pdf_path); |
| HMODULE pdf_module = GetModuleHandle(pdf_path.value().c_str()); |
| if (!pdf_module) |
| return false; |
| RenderPDFPageToDCProc render_proc = |
| reinterpret_cast<RenderPDFPageToDCProc>( |
| GetProcAddress(pdf_module, "RenderPDFPageToDC")); |
| if (!render_proc) |
| return false; |
| #endif // defined(OS_WIN) |
| |
| unsigned char* pdf_output = NULL; |
| int32 output_size = 0; |
| NPPrintPageNumberRange page_range; |
| #if defined(OS_LINUX) |
| // On Linux we will try and output all pages as PDF in the first call to |
| // PrintPage. This is a temporary hack. |
| // TODO(sanjeevr): Remove this hack and fix this by changing the print |
| // interfaces for WebFrame and WebPlugin. |
| if (page_number != 0) |
| return pdf_output_done_; |
| page_range.firstPageNumber = 0; |
| page_range.lastPageNumber = num_pages_ - 1; |
| #else // defined(OS_LINUX) |
| page_range.firstPageNumber = page_range.lastPageNumber = page_number; |
| #endif // defined(OS_LINUX) |
| NPError err = print_extensions->printPagesAsPDF(instance()->npp(), |
| &page_range, 1, |
| &pdf_output, &output_size); |
| if (err != NPERR_NO_ERROR) |
| return false; |
| |
| bool ret = false; |
| #if defined(OS_LINUX) |
| // On Linux we need to get the backing PdfPsMetafile and write the bits |
| // directly. |
| cairo_t* context = canvas->beginPlatformPaint(); |
| printing::NativeMetafile* metafile = |
| printing::NativeMetafile::FromCairoContext(context); |
| DCHECK(metafile); |
| if (metafile) { |
| ret = metafile->SetRawData(pdf_output, output_size); |
| if (ret) |
| pdf_output_done_ = true; |
| } |
| canvas->endPlatformPaint(); |
| #elif defined(OS_MACOSX) |
| printing::NativeMetafile metafile; |
| // Create a PDF metafile and render from there into the passed in context. |
| if (metafile.Init(pdf_output, output_size)) { |
| // Flip the transform. |
| CGContextSaveGState(canvas); |
| CGContextTranslateCTM(canvas, 0, current_printable_area_.height()); |
| CGContextScaleCTM(canvas, 1.0, -1.0); |
| ret = metafile.RenderPage(1, canvas, current_printable_area_.ToCGRect(), |
| true, false, true, true); |
| CGContextRestoreGState(canvas); |
| } |
| #elif defined(OS_WIN) |
| // On Windows, we now need to render the PDF to the DC that backs the |
| // supplied canvas. |
| skia::VectorPlatformDevice& device = |
| static_cast<skia::VectorPlatformDevice&>( |
| canvas->getTopPlatformDevice()); |
| HDC dc = device.getBitmapDC(); |
| gfx::Size size_in_pixels; |
| size_in_pixels.set_width( |
| printing::ConvertUnit(current_printable_area_.width(), |
| static_cast<int>(kPointsPerInch), |
| current_printer_dpi_)); |
| size_in_pixels.set_height( |
| printing::ConvertUnit(current_printable_area_.height(), |
| static_cast<int>(kPointsPerInch), |
| current_printer_dpi_)); |
| // We need to render using the actual printer DPI (rendering to a smaller |
| // set of pixels leads to a blurry output). However, we need to counter the |
| // scaling up that will happen in the browser. |
| XFORM xform = {0}; |
| xform.eM11 = xform.eM22 = kPointsPerInch / current_printer_dpi_; |
| ModifyWorldTransform(dc, &xform, MWT_LEFTMULTIPLY); |
| |
| ret = render_proc(pdf_output, output_size, 0, dc, current_printer_dpi_, |
| current_printer_dpi_, 0, 0, size_in_pixels.width(), |
| size_in_pixels.height(), true, false, true, true); |
| #endif // defined(OS_WIN) |
| |
| NPAPI::PluginHost::Singleton()->host_functions()->memfree(pdf_output); |
| return ret; |
| } |
| |
| bool WebPluginDelegatePepper::PrintPage(int page_number, |
| WebKit::WebCanvas* canvas) { |
| NPPPrintExtensions* print_extensions = GetPrintExtensions(); |
| if (!print_extensions) |
| return false; |
| |
| // First try and use vector print. |
| if (VectorPrintPage(page_number, canvas)) |
| return true; |
| |
| DCHECK(!current_printable_area_.IsEmpty()); |
| |
| // Calculate the width and height needed for the raster image. |
| NPRect np_printable_area = {0}; |
| np_printable_area.left = current_printable_area_.x(); |
| np_printable_area.top = current_printable_area_.y(); |
| np_printable_area.right = |
| current_printable_area_.x() + current_printable_area_.width(); |
| np_printable_area.bottom = |
| current_printable_area_.y() + current_printable_area_.height(); |
| gfx::Size size_in_pixels; |
| if (!CalculatePrintedPageDimensions(page_number, print_extensions, |
| &size_in_pixels)) { |
| return false; |
| } |
| |
| // Now print the page onto a 2d device context. |
| scoped_ptr<Graphics2DDeviceContext> g2d(new Graphics2DDeviceContext(this)); |
| NPDeviceContext2DConfig config; |
| NPDeviceContext2D context; |
| gfx::Rect surface_rect(size_in_pixels); |
| NPError err = g2d->Initialize(surface_rect, &config, &context); |
| if (err != NPERR_NO_ERROR) { |
| NOTREACHED(); |
| return false; |
| } |
| err = print_extensions->printPageRaster(instance()->npp(), page_number, |
| &context); |
| if (err != NPERR_NO_ERROR) |
| return false; |
| |
| SkBitmap committed; |
| committed.setConfig(SkBitmap::kARGB_8888_Config, size_in_pixels.width(), |
| size_in_pixels.height()); |
| committed.allocPixels(); |
| err = g2d->Flush(&committed, &context, NULL, instance()->npp(), NULL); |
| if (err != NPERR_NO_ERROR) { |
| NOTREACHED(); |
| return false; |
| } |
| // Draw the printed image into the supplied canvas. |
| SkIRect src_rect; |
| src_rect.set(0, 0, size_in_pixels.width(), size_in_pixels.height()); |
| SkRect dest_rect; |
| dest_rect.set(SkIntToScalar(current_printable_area_.x()), |
| SkIntToScalar(current_printable_area_.y()), |
| SkIntToScalar(current_printable_area_.x() + |
| current_printable_area_.width()), |
| SkIntToScalar(current_printable_area_.y() + |
| current_printable_area_.height())); |
| bool draw_to_canvas = true; |
| #if defined(OS_WIN) |
| // Since this is a raster output, the size of the bitmap can be |
| // huge (especially at high printer DPIs). On Windows, this can |
| // result in a HUGE EMF (on Mac and Linux the output goes to PDF |
| // which appears to Flate compress the bitmap). So, if this bitmap |
| // is larger than 20 MB, we save the bitmap as a JPEG into the EMF |
| // DC. Note: We chose JPEG over PNG because JPEG compression seems |
| // way faster (about 4 times faster). |
| static const int kCompressionThreshold = 20 * 1024 * 1024; |
| if (committed.getSize() > kCompressionThreshold) { |
| DrawJPEGToPlatformDC(committed, current_printable_area_, canvas); |
| draw_to_canvas = false; |
| } |
| #endif // defined(OS_WIN) |
| #if defined(OS_MACOSX) |
| draw_to_canvas = false; |
| DrawSkBitmapToCanvas(committed, canvas, current_printable_area_, |
| current_printable_area_.height()); |
| // See comments in the header file. |
| last_printed_page_ = committed; |
| #else // defined(OS_MACOSX) |
| if (draw_to_canvas) |
| canvas->drawBitmapRect(committed, &src_rect, dest_rect); |
| #endif // defined(OS_MACOSX) |
| return true; |
| } |
| |
| void WebPluginDelegatePepper::PrintEnd() { |
| NPPPrintExtensions* print_extensions = GetPrintExtensions(); |
| if (print_extensions) |
| print_extensions->printEnd(instance()->npp()); |
| current_printable_area_ = gfx::Rect(); |
| current_printer_dpi_ = -1; |
| #if defined(OS_MACOSX) |
| last_printed_page_ = SkBitmap(); |
| #elif defined(OS_LINUX) |
| num_pages_ = 0; |
| pdf_output_done_ = false; |
| #endif // defined(OS_LINUX) |
| } |
| |
| WebPluginDelegatePepper::WebPluginDelegatePepper( |
| const base::WeakPtr<RenderView>& render_view, |
| NPAPI::PluginInstance *instance) |
| : render_view_(render_view), |
| plugin_(NULL), |
| instance_(instance), |
| nested_delegate_(NULL), |
| current_printer_dpi_(-1), |
| #if defined (OS_LINUX) |
| num_pages_(0), |
| pdf_output_done_(false), |
| #endif // (OS_LINUX) |
| #if defined(ENABLE_GPU) |
| command_buffer_(NULL), |
| method_factory3d_(ALLOW_THIS_IN_INITIALIZER_LIST(this)), |
| #endif |
| find_identifier_(-1), |
| current_choose_file_callback_(NULL), |
| current_choose_file_user_data_(NULL) { |
| // For now we keep a window struct, although it isn't used. |
| memset(&window_, 0, sizeof(window_)); |
| // All Pepper plugins are windowless and transparent. |
| // TODO(sehr): disable resetting these NPPVs by plugins. |
| instance->set_windowless(true); |
| instance->set_transparent(true); |
| } |
| |
| WebPluginDelegatePepper::~WebPluginDelegatePepper() { |
| DestroyInstance(); |
| |
| if (render_view_) |
| render_view_->OnPepperPluginDestroy(this); |
| } |
| |
| void WebPluginDelegatePepper::ForwardSetWindow() { |
| window_.clipRect.top = clip_rect_.y(); |
| window_.clipRect.left = clip_rect_.x(); |
| window_.clipRect.bottom = clip_rect_.y() + clip_rect_.height(); |
| window_.clipRect.right = clip_rect_.x() + clip_rect_.width(); |
| window_.height = window_rect_.height(); |
| window_.width = window_rect_.width(); |
| window_.x = window_rect_.x(); |
| window_.y = window_rect_.y(); |
| window_.type = NPWindowTypeDrawable; |
| instance()->NPP_SetWindow(&window_); |
| } |
| |
| void WebPluginDelegatePepper::PluginDestroyed() { |
| delete this; |
| } |
| |
| void WebPluginDelegatePepper::Paint(WebKit::WebCanvas* canvas, |
| const gfx::Rect& rect) { |
| if (nested_delegate_) { |
| // TODO(apatrick): The GPU plugin will render to an offscreen render target. |
| // Need to copy it to the screen here. |
| } else { |
| // Blit from background_context to context. |
| if (!committed_bitmap_.isNull()) { |
| #if defined(OS_MACOSX) |
| DrawSkBitmapToCanvas(committed_bitmap_, canvas, window_rect_, |
| static_cast<int>(CGBitmapContextGetHeight(canvas))); |
| #else |
| gfx::Point origin(window_rect_.origin().x(), window_rect_.origin().y()); |
| canvas->drawBitmap(committed_bitmap_, |
| SkIntToScalar(window_rect_.origin().x()), |
| SkIntToScalar(window_rect_.origin().y())); |
| #endif |
| } |
| } |
| } |
| |
| void WebPluginDelegatePepper::Print(gfx::NativeDrawingContext context) { |
| NOTIMPLEMENTED(); |
| } |
| |
| void WebPluginDelegatePepper::InstallMissingPlugin() { |
| NOTIMPLEMENTED(); |
| } |
| |
| void WebPluginDelegatePepper::SetFocus(bool focused) { |
| if (!focused) |
| return; |
| |
| NPPepperEvent npevent; |
| |
| npevent.type = NPEventType_Focus; |
| npevent.size = sizeof(npevent); |
| // TODO(sehr): what timestamp should this have? |
| npevent.timeStampSeconds = 0.0; |
| // Currently this API only supports gaining focus. |
| npevent.u.focus.value = 1; |
| instance()->NPP_HandleEvent(&npevent); |
| } |
| |
| // Anonymous namespace for functions converting WebInputEvents to NPAPI types. |
| namespace { |
| NPEventTypes ConvertEventTypes(WebInputEvent::Type wetype) { |
| switch (wetype) { |
| case WebInputEvent::MouseDown: |
| return NPEventType_MouseDown; |
| case WebInputEvent::MouseUp: |
| return NPEventType_MouseUp; |
| case WebInputEvent::MouseMove: |
| return NPEventType_MouseMove; |
| case WebInputEvent::MouseEnter: |
| return NPEventType_MouseEnter; |
| case WebInputEvent::MouseLeave: |
| return NPEventType_MouseLeave; |
| case WebInputEvent::MouseWheel: |
| return NPEventType_MouseWheel; |
| case WebInputEvent::RawKeyDown: |
| return NPEventType_RawKeyDown; |
| case WebInputEvent::KeyDown: |
| return NPEventType_KeyDown; |
| case WebInputEvent::KeyUp: |
| return NPEventType_KeyUp; |
| case WebInputEvent::Char: |
| return NPEventType_Char; |
| case WebInputEvent::Undefined: |
| default: |
| return NPEventType_Undefined; |
| } |
| } |
| |
| void BuildKeyEvent(const WebInputEvent* event, NPPepperEvent* npevent) { |
| const WebKeyboardEvent* key_event = |
| reinterpret_cast<const WebKeyboardEvent*>(event); |
| npevent->u.key.modifier = key_event->modifiers; |
| npevent->u.key.normalizedKeyCode = key_event->windowsKeyCode; |
| } |
| |
| void BuildCharEvent(const WebInputEvent* event, NPPepperEvent* npevent) { |
| const WebKeyboardEvent* key_event = |
| reinterpret_cast<const WebKeyboardEvent*>(event); |
| npevent->u.character.modifier = key_event->modifiers; |
| // For consistency, check that the sizes of the texts agree. |
| DCHECK(sizeof(npevent->u.character.text) == sizeof(key_event->text)); |
| DCHECK(sizeof(npevent->u.character.unmodifiedText) == |
| sizeof(key_event->unmodifiedText)); |
| for (size_t i = 0; i < WebKeyboardEvent::textLengthCap; ++i) { |
| npevent->u.character.text[i] = key_event->text[i]; |
| npevent->u.character.unmodifiedText[i] = key_event->unmodifiedText[i]; |
| } |
| } |
| |
| void BuildMouseEvent(const WebInputEvent* event, NPPepperEvent* npevent) { |
| const WebMouseEvent* mouse_event = |
| reinterpret_cast<const WebMouseEvent*>(event); |
| npevent->u.mouse.modifier = mouse_event->modifiers; |
| npevent->u.mouse.button = mouse_event->button; |
| npevent->u.mouse.x = mouse_event->x; |
| npevent->u.mouse.y = mouse_event->y; |
| npevent->u.mouse.clickCount = mouse_event->clickCount; |
| } |
| |
| void BuildMouseWheelEvent(const WebInputEvent* event, NPPepperEvent* npevent) { |
| const WebMouseWheelEvent* mouse_wheel_event = |
| reinterpret_cast<const WebMouseWheelEvent*>(event); |
| npevent->u.wheel.modifier = mouse_wheel_event->modifiers; |
| npevent->u.wheel.deltaX = mouse_wheel_event->deltaX; |
| npevent->u.wheel.deltaY = mouse_wheel_event->deltaY; |
| npevent->u.wheel.wheelTicksX = mouse_wheel_event->wheelTicksX; |
| npevent->u.wheel.wheelTicksY = mouse_wheel_event->wheelTicksY; |
| npevent->u.wheel.scrollByPage = mouse_wheel_event->scrollByPage; |
| } |
| } // namespace |
| |
| bool WebPluginDelegatePepper::HandleInputEvent(const WebInputEvent& event, |
| WebCursorInfo* cursor_info) { |
| NPPepperEvent npevent; |
| |
| npevent.type = ConvertEventTypes(event.type); |
| npevent.size = sizeof(npevent); |
| npevent.timeStampSeconds = event.timeStampSeconds; |
| switch (npevent.type) { |
| case NPEventType_Undefined: |
| return false; |
| case NPEventType_MouseDown: |
| case NPEventType_MouseUp: |
| case NPEventType_MouseMove: |
| case NPEventType_MouseEnter: |
| case NPEventType_MouseLeave: |
| BuildMouseEvent(&event, &npevent); |
| break; |
| case NPEventType_MouseWheel: |
| BuildMouseWheelEvent(&event, &npevent); |
| break; |
| case NPEventType_RawKeyDown: |
| case NPEventType_KeyDown: |
| case NPEventType_KeyUp: |
| BuildKeyEvent(&event, &npevent); |
| break; |
| case NPEventType_Char: |
| BuildCharEvent(&event, &npevent); |
| break; |
| case NPEventType_Minimize: |
| case NPEventType_Focus: |
| case NPEventType_Device: |
| // NOTIMPLEMENTED(); |
| break; |
| } |
| bool rv = instance()->NPP_HandleEvent(&npevent) != 0; |
| if (cursor_.get()) |
| *cursor_info = *cursor_; |
| return rv; |
| } |
| |
| #if defined(ENABLE_GPU) |
| |
| void WebPluginDelegatePepper::ScheduleHandleRepaint( |
| NPP npp, NPDeviceContext3D* context) { |
| command_buffer_->SetNotifyRepaintTask(method_factory3d_.NewRunnableMethod( |
| &WebPluginDelegatePepper::ForwardHandleRepaint, |
| npp, |
| context)); |
| } |
| |
| void WebPluginDelegatePepper::ForwardHandleRepaint( |
| NPP npp, NPDeviceContext3D* context) { |
| if (context->repaintCallback) |
| context->repaintCallback(npp, context); |
| ScheduleHandleRepaint(npp, context); |
| } |
| |
| void WebPluginDelegatePepper::Synchronize3DContext( |
| NPDeviceContext3D* context, |
| gpu::CommandBuffer::State state) { |
| context->getOffset = state.get_offset; |
| context->putOffset = state.put_offset; |
| context->token = state.token; |
| context->error = static_cast<NPDeviceContext3DError>(state.error); |
| } |
| |
| void WebPluginDelegatePepper::Device3DUpdateState( |
| NPP npp, |
| NPDeviceContext3D* context, |
| NPDeviceFlushContextCallbackPtr callback, |
| void* user_data) { |
| if (command_buffer_) { |
| Synchronize3DContext(context, command_buffer_->GetLastState()); |
| if (callback) |
| callback(npp, context, NPERR_NO_ERROR, user_data); |
| } |
| } |
| |
| #endif // ENABLE_GPU |
| |
| void WebPluginDelegatePepper::SendNestedDelegateGeometryToBrowser( |
| const gfx::Rect& window_rect, |
| const gfx::Rect& clip_rect) { |
| // Inform the browser about the location of the plugin on the page. |
| // It appears that initially the plugin does not get laid out correctly -- |
| // possibly due to lazy creation of the nested delegate. |
| if (!nested_delegate_ || |
| !nested_delegate_->GetPluginWindowHandle() || |
| !render_view_) { |
| return; |
| } |
| |
| webkit_glue::WebPluginGeometry geom; |
| geom.window = nested_delegate_->GetPluginWindowHandle(); |
| geom.window_rect = window_rect; |
| geom.clip_rect = clip_rect; |
| // Rects_valid must be true for this to work in the Gtk port; |
| // hopefully not having the cutout rects will not cause incorrect |
| // clipping. |
| geom.rects_valid = true; |
| geom.visible = true; |
| render_view_->DidMovePlugin(geom); |
| } |
| |
| bool WebPluginDelegatePepper::CalculatePrintedPageDimensions( |
| int page_number, |
| NPPPrintExtensions* print_extensions, |
| gfx::Size* page_dimensions) { |
| int32 width_in_pixels = 0; |
| int32 height_in_pixels = 0; |
| NPError err = print_extensions->getRasterDimensions( |
| instance()->npp(), page_number, &width_in_pixels, &height_in_pixels); |
| if (err != NPERR_NO_ERROR) |
| return false; |
| |
| DCHECK(width_in_pixels && height_in_pixels); |
| page_dimensions->SetSize(width_in_pixels, height_in_pixels); |
| return true; |
| } |
| |
| NPPPrintExtensions* WebPluginDelegatePepper::GetPrintExtensions() { |
| NPPPrintExtensions* ret = NULL; |
| NPPExtensions* extensions = NULL; |
| instance()->NPP_GetValue(NPPVPepperExtensions, &extensions); |
| if (extensions && extensions->getPrintExtensions) |
| ret = extensions->getPrintExtensions(instance()->npp()); |
| return ret; |
| } |
| |
| NPPFindExtensions* WebPluginDelegatePepper::GetFindExtensions() { |
| NPPFindExtensions* ret = NULL; |
| NPPExtensions* extensions = NULL; |
| instance()->NPP_GetValue(NPPVPepperExtensions, &extensions); |
| if (extensions && extensions->getFindExtensions) |
| ret = extensions->getFindExtensions(instance()->npp()); |
| return ret; |
| } |
| |
| #if defined(OS_WIN) |
| bool WebPluginDelegatePepper::DrawJPEGToPlatformDC( |
| const SkBitmap& bitmap, |
| const gfx::Rect& printable_area, |
| WebKit::WebCanvas* canvas) { |
| skia::VectorPlatformDevice& device = |
| static_cast<skia::VectorPlatformDevice&>( |
| canvas->getTopPlatformDevice()); |
| HDC dc = device.getBitmapDC(); |
| // TODO(sanjeevr): This is a temporary hack. If we output a JPEG |
| // to the EMF, the EnumEnhMetaFile call fails in the browser |
| // process. The failure also happens if we output nothing here. |
| // We need to investigate the reason for this failure and fix it. |
| // In the meantime this temporary hack of drawing an empty |
| // rectangle in the DC gets us by. |
| Rectangle(dc, 0, 0, 0, 0); |
| |
| // Ideally we should add JPEG compression to the VectorPlatformDevice class |
| // However, Skia currently has no JPEG compression code and we cannot |
| // depend on gfx/jpeg_codec.h in Skia. So we do the compression here. |
| SkAutoLockPixels lock(bitmap); |
| DCHECK(bitmap.getConfig() == SkBitmap::kARGB_8888_Config); |
| const uint32_t* pixels = |
| static_cast<const uint32_t*>(bitmap.getPixels()); |
| std::vector<unsigned char> compressed_image; |
| base::TimeTicks start_time = base::TimeTicks::Now(); |
| bool encoded = gfx::JPEGCodec::Encode( |
| reinterpret_cast<const unsigned char*>(pixels), |
| gfx::JPEGCodec::FORMAT_BGRA, bitmap.width(), bitmap.height(), |
| static_cast<int>(bitmap.rowBytes()), 100, &compressed_image); |
| UMA_HISTOGRAM_TIMES("PepperPluginPrint.RasterBitmapCompressTime", |
| base::TimeTicks::Now() - start_time); |
| if (!encoded) { |
| NOTREACHED(); |
| return false; |
| } |
| BITMAPINFOHEADER bmi = {0}; |
| gfx::CreateBitmapHeader(bitmap.width(), bitmap.height(), &bmi); |
| bmi.biCompression = BI_JPEG; |
| bmi.biSizeImage = compressed_image.size(); |
| bmi.biHeight = -bmi.biHeight; |
| StretchDIBits(dc, printable_area.x(), printable_area.y(), |
| printable_area.width(), printable_area.height(), |
| 0, 0, bitmap.width(), bitmap.height(), |
| &compressed_image.front(), |
| reinterpret_cast<const BITMAPINFO*>(&bmi), |
| DIB_RGB_COLORS, SRCCOPY); |
| return true; |
| } |
| #endif // OS_WIN |
| |
| #if defined(OS_MACOSX) |
| void WebPluginDelegatePepper::DrawSkBitmapToCanvas( |
| const SkBitmap& bitmap, WebKit::WebCanvas* canvas, |
| const gfx::Rect& dest_rect, |
| int canvas_height) { |
| SkAutoLockPixels lock(bitmap); |
| DCHECK(bitmap.getConfig() == SkBitmap::kARGB_8888_Config); |
| scoped_cftyperef<CGDataProviderRef> data_provider( |
| CGDataProviderCreateWithData( |
| NULL, bitmap.getAddr32(0, 0), |
| bitmap.rowBytes() * bitmap.height(), NULL)); |
| scoped_cftyperef<CGImageRef> image( |
| CGImageCreate( |
| bitmap.width(), bitmap.height(), |
| 8, 32, bitmap.rowBytes(), |
| mac_util::GetSystemColorSpace(), |
| kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host, |
| data_provider, NULL, false, kCGRenderingIntentDefault)); |
| |
| // Flip the transform |
| CGContextSaveGState(canvas); |
| CGContextTranslateCTM(canvas, 0, canvas_height); |
| CGContextScaleCTM(canvas, 1.0, -1.0); |
| |
| CGRect bounds; |
| bounds.origin.x = dest_rect.x(); |
| bounds.origin.y = canvas_height - dest_rect.y() - dest_rect.height(); |
| bounds.size.width = dest_rect.width(); |
| bounds.size.height = dest_rect.height(); |
| |
| CGContextDrawImage(canvas, bounds, image); |
| CGContextRestoreGState(canvas); |
| } |
| #endif // defined(OS_MACOSX) |