// 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 #include #if defined(OS_LINUX) #include #endif #include "base/file_path.h" #include "base/file_util.h" #include "base/keyboard_codes.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 using gpu::Buffer; 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 { gpu::CommandBuffer* command_buffer; 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& render_view) { scoped_refptr 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 instance = plugin_lib->CreateInstance(mime_type); return new WebPluginDelegatePepper(render_view, instance.get()); } void WebPluginDelegatePepper::didChooseFile( const WebKit::WebVector& 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 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 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(pointers_to_strings.size()), current_choose_file_user_data_); } } bool WebPluginDelegatePepper::Initialize( const GURL& url, const std::vector& arg_names, const std::vector& arg_values, WebPlugin* plugin, bool load_manually) { plugin_ = plugin; instance_->set_web_plugin(plugin_); int argc = 0; scoped_array argn(new char*[arg_names.size()]); scoped_array argv(new char*[arg_names.size()]); for (size_t i = 0; i < arg_names.size(); ++i) { argn[argc] = const_cast(arg_names[i].c_str()); argv[argc] = const_cast(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); if (total == 0) { render_view_->ReportNoFindInPageResults(find_identifier_); } else { 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(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(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(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 g2d(new Graphics2DDeviceContext(this)); NPError status = g2d->Initialize(window_rect_, config, context); if (NPERR_NO_ERROR == status) { context->reserved = reinterpret_cast( 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(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(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(context->reserved))) { return NPERR_INVALID_PARAM; } graphic2d_contexts_.Remove(reinterpret_cast(context->reserved)); memset(context, 0, sizeof(NPDeviceContext2D)); return NPERR_NO_ERROR; } Graphics2DDeviceContext* WebPluginDelegatePepper::GetGraphicsContext( NPDeviceContext2D* context) { return graphic2d_contexts_.Lookup( reinterpret_cast(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::vector(), 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(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((*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( 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 audio(new AudioDeviceContext()); NPError status = audio->Initialize(render_view_->audio_message_filter(), config, context); if (NPERR_NO_ERROR == status) { context->reserved = reinterpret_cast(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(context->reserved)); if (!ctx) return NPERR_INVALID_PARAM; *value = reinterpret_cast(ctx->shared_memory()); return NPERR_NO_ERROR; } else if (state == NPExtensionsReservedStateSharedMemorySize) { if (!context) return NPERR_INVALID_PARAM; AudioDeviceContext* ctx = audio_contexts_.Lookup( reinterpret_cast(context->reserved)); if (!ctx) return NPERR_INVALID_PARAM; *value = static_cast(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(context->reserved)); if (!ctx) return NPERR_INVALID_PARAM; *value = reinterpret_cast(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(context->reserved))) { return NPERR_INVALID_PARAM; } audio_contexts_.Remove(reinterpret_cast(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( 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( canvas->getTopPlatformDevice()); HDC dc = device.getBitmapDC(); gfx::Size size_in_pixels; size_in_pixels.set_width( printing::ConvertUnit(current_printable_area_.width(), static_cast(kPointsPerInch), current_printer_dpi_)); size_in_pixels.set_height( printing::ConvertUnit(current_printable_area_.height(), static_cast(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 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& 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), #endif find_identifier_(-1), method_factory3d_(ALLOW_THIS_IN_INITIALIZER_LIST(this)), 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(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(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(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(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(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(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( 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(bitmap.getPixels()); std::vector compressed_image; base::TimeTicks start_time = base::TimeTicks::Now(); bool encoded = gfx::JPEGCodec::Encode( reinterpret_cast(pixels), gfx::JPEGCodec::FORMAT_BGRA, bitmap.width(), bitmap.height(), static_cast(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(&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 data_provider( CGDataProviderCreateWithData( NULL, bitmap.getAddr32(0, 0), bitmap.rowBytes() * bitmap.height(), NULL)); scoped_cftyperef 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)