From 6bf1a81cdfcc48f20cf53c2601f4456f2d9d30ef Mon Sep 17 00:00:00 2001 From: "amanda@chromium.org" Date: Sat, 11 Jul 2009 01:57:28 +0000 Subject: Wire up windowless plugins. Mostly Mac related, some cross platform aspects. BUG=10809 TEST=none Review URL: http://codereview.chromium.org/113637 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@20453 0039d316-1c4b-4281-b951-d872f2087c98 --- app/app.gyp | 1 + app/gfx/canvas.cc | 10 - app/gfx/canvas_mac.mm | 79 +++++ chrome/common/plugin_messages_internal.h | 4 +- chrome/common/transport_dib_mac.cc | 2 +- chrome/plugin/plugin_main.cc | 63 +++- chrome/plugin/webplugin_delegate_stub.cc | 6 +- chrome/plugin/webplugin_delegate_stub.h | 4 +- chrome/plugin/webplugin_proxy.cc | 95 +++++- chrome/plugin/webplugin_proxy.h | 20 +- chrome/renderer/render_view.cc | 13 +- chrome/renderer/webplugin_delegate_proxy.cc | 181 ++++++---- chrome/renderer/webplugin_delegate_proxy.h | 21 +- webkit/glue/plugins/plugin_host.cc | 24 +- webkit/glue/plugins/webplugin_delegate_impl.h | 4 +- webkit/glue/plugins/webplugin_delegate_impl_mac.mm | 375 ++++++++++++--------- 16 files changed, 611 insertions(+), 291 deletions(-) create mode 100644 app/gfx/canvas_mac.mm diff --git a/app/app.gyp b/app/app.gyp index faced76..5c989ae 100644 --- a/app/app.gyp +++ b/app/app.gyp @@ -68,6 +68,7 @@ 'gfx/canvas.cc', 'gfx/canvas.h', 'gfx/canvas_linux.cc', + 'gfx/canvas_mac.mm', 'gfx/canvas_win.cc', 'gfx/font.h', 'gfx/font_gtk.cc', diff --git a/app/gfx/canvas.cc b/app/gfx/canvas.cc index d3b55ab..2cef3de 100644 --- a/app/gfx/canvas.cc +++ b/app/gfx/canvas.cc @@ -221,16 +221,6 @@ void Canvas::DrawStringInt(const std::wstring& text, l10n_util::DefaultCanvasTextAlignment()); } -#if defined(OS_MACOSX) -void Canvas::DrawStringInt(const std::wstring& text, - const gfx::Font& font, - const SkColor& color, - int x, int y, int w, int h, - int flags) { - NOTIMPLEMENTED(); -} -#endif - void Canvas::TileImageInt(const SkBitmap& bitmap, int x, int y, int w, int h) { TileImageInt(bitmap, 0, 0, x, y, w, h); } diff --git a/app/gfx/canvas_mac.mm b/app/gfx/canvas_mac.mm new file mode 100644 index 0000000..b766b32 --- /dev/null +++ b/app/gfx/canvas_mac.mm @@ -0,0 +1,79 @@ +// Copyright (c) 2006-2008 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. + +#import + +#include "app/gfx/canvas.h" + +#include "app/gfx/font.h" +#include "app/l10n_util.h" +#include "base/gfx/rect.h" +#include "base/scoped_cftyperef.h" +#include "base/sys_string_conversions.h" +#include "third_party/skia/include/core/SkShader.h" + +namespace gfx { + +Canvas::Canvas(int width, int height, bool is_opaque) + : skia::PlatformCanvas(width, height, is_opaque) { +} + +Canvas::Canvas() : skia::PlatformCanvas() { +} + +Canvas::~Canvas() { +} + +// static +void Canvas::SizeStringInt(const std::wstring& text, + const gfx::Font& font, + int *width, int *height, int flags) { + *width = font.GetStringWidth(text); + *height = font.height(); +} + +void Canvas::DrawStringInt(const std::wstring& text, const gfx::Font& font, + const SkColor& color, int x, int y, int w, int h, + int flags) { + if (!IntersectsClipRectInt(x, y, w, h)) + return; + + CGContextRef context = beginPlatformPaint(); + CGContextSaveGState(context); + + NSColor* ns_color = [NSColor colorWithDeviceRed:SkColorGetR(color) / 255.0 + green:SkColorGetG(color) / 255.0 + blue:SkColorGetB(color) / 255.0 + alpha:1.0]; + NSMutableParagraphStyle *ns_style = + [[[NSParagraphStyle alloc] init] autorelease]; + if (flags & TEXT_ALIGN_CENTER) + [ns_style setAlignment:NSCenterTextAlignment]; + // TODO(awalker): Implement the rest of the Canvas text flags + + NSDictionary* attributes = + [NSDictionary dictionaryWithObjectsAndKeys: + font.nativeFont(), NSFontAttributeName, + ns_color, NSForegroundColorAttributeName, + ns_style, NSParagraphStyleAttributeName, + nil]; + + NSAttributedString* ns_string = + [[[NSAttributedString alloc] initWithString:base::SysWideToNSString(text) + attributes:attributes] autorelease]; + scoped_cftyperef framesetter( + CTFramesetterCreateWithAttributedString(reinterpret_cast(ns_string))); + + CGRect text_bounds = CGRectMake(x, y, w, h); + CGMutablePathRef path = CGPathCreateMutable(); + CGPathAddRect(path, NULL, text_bounds); + + scoped_cftyperef frame( + CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), path, NULL)); + CTFrameDraw(frame, context); + CGContextRestoreGState(context); + endPlatformPaint(); +} + +} // namespace gfx diff --git a/chrome/common/plugin_messages_internal.h b/chrome/common/plugin_messages_internal.h index dc853c2..974de00 100644 --- a/chrome/common/plugin_messages_internal.h +++ b/chrome/common/plugin_messages_internal.h @@ -162,8 +162,8 @@ IPC_BEGIN_MESSAGES(Plugin) IPC_MESSAGE_ROUTED4(PluginMsg_UpdateGeometry, gfx::Rect /* window_rect */, gfx::Rect /* clip_rect */, - TransportDIB::Id /* windowless_buffer */, - TransportDIB::Id /* background_buffer */) + TransportDIB::Handle /* windowless_buffer */, + TransportDIB::Handle /* background_buffer */) IPC_SYNC_MESSAGE_ROUTED0_0(PluginMsg_SetFocus) diff --git a/chrome/common/transport_dib_mac.cc b/chrome/common/transport_dib_mac.cc index 9ce5e4f..da66085 100644 --- a/chrome/common/transport_dib_mac.cc +++ b/chrome/common/transport_dib_mac.cc @@ -45,7 +45,7 @@ TransportDIB* TransportDIB::Map(TransportDIB::Handle handle) { if (!dib->shared_memory_.Map(st.st_size)) { delete dib; HANDLE_EINTR(close(handle.fd)); - return false; + return NULL; } dib->size_ = st.st_size; diff --git a/chrome/plugin/plugin_main.cc b/chrome/plugin/plugin_main.cc index fb8880f..4cc7911 100644 --- a/chrome/plugin/plugin_main.cc +++ b/chrome/plugin/plugin_main.cc @@ -27,6 +27,47 @@ #include "base/global_descriptors_posix.h" #endif +#if defined(OS_MACOSX) + +// To support Mac NPAPI plugins that use the Carbon event model (i.e., most +// shipping plugins for MacOS X 10.5 and earlier), we need some way for the +// Carbon event dispatcher to run, even though the plugin host process itself +// does not use Carbon events. Rather than give control to the standard +// Carbon event loop, we schedule a periodic task on the main thread which +// empties the Carbon event queue every 20ms (chosen to match how often Safari +// does the equivalent operation). This allows plugins to receive idle events +// and schedule Carbon timers without swamping the CPU. If, in the future, +// we remove support for the Carbon event model and only support the Cocoa +// event model, this can be removed. Note that this approach does not install +// standard application event handlers for the menubar, AppleEvents, and so on. +// This is intentional, since the plugin process is not actually an application +// with its own UI elements--all rendering and event handling happens via IPC +// to the renderer process which invoked it. + +namespace { + +const int kPluginUpdateIntervalMs = 20; // 20ms = 50Hz + +void PluginCarbonEventTask() { + EventRef theEvent; + EventTargetRef theTarget; + + theTarget = GetEventDispatcherTarget(); + + // Dispatch any pending events. but do not block if there are no events. + while (ReceiveNextEvent(0, NULL, kEventDurationNoWait, + true, &theEvent) == noErr) { + SendEventToEventTarget (theEvent, theTarget); + ReleaseEvent(theEvent); + } + + MessageLoop::current()->PostDelayedTask(FROM_HERE, + NewRunnableFunction(PluginCarbonEventTask), kPluginUpdateIntervalMs); +} + +} +#endif + // main() routine for running as the plugin process. int PluginMain(const MainFunctionParams& parameters) { // The main thread of the plugin services IO. @@ -37,9 +78,9 @@ int PluginMain(const MainFunctionParams& parameters) { // Initialize the SystemMonitor base::SystemMonitor::Start(); -#if defined(OS_WIN) const CommandLine& parsed_command_line = parameters.command_line_; +#if defined(OS_WIN) sandbox::TargetServices* target_services = parameters.sandbox_info_.TargetServices(); @@ -59,16 +100,32 @@ int PluginMain(const MainFunctionParams& parameters) { DCHECK(sandbox_test_module); } } - +#endif if (parsed_command_line.HasSwitch(switches::kPluginStartupDialog)) { +#if defined(OS_WIN) std::wstring title = chrome::kBrowserAppName; title += L" plugin"; // makes attaching to process easier win_util::MessageBox(NULL, L"plugin starting...", title, MB_OK | MB_SETFOREGROUND); - } +#elif defined(OS_MACOSX) + // TODO(playmobil): In the long term, overriding this flag doesn't seem + // right, either use our own flag or open a dialog we can use. + // This is just to ease debugging in the interim. + LOG(WARNING) << "Plugin (" + << getpid() + << ") paused waiting for debugger to attach @ pid"; + pause(); #else NOTIMPLEMENTED() << " non-windows startup, plugin startup dialog etc."; #endif + } + +#if defined(OS_MACOSX) + // Spin off a consumer for the native (Carbon) event stream so + // that plugin timers, event handlers, etc. will work properly. + MessageLoop::current()->PostDelayedTask(FROM_HERE, + NewRunnableFunction(PluginCarbonEventTask), kPluginUpdateIntervalMs); +#endif { ChildProcess plugin_process(new PluginThread()); diff --git a/chrome/plugin/webplugin_delegate_stub.cc b/chrome/plugin/webplugin_delegate_stub.cc index 542f0a3..a8a42b0 100644 --- a/chrome/plugin/webplugin_delegate_stub.cc +++ b/chrome/plugin/webplugin_delegate_stub.cc @@ -272,11 +272,11 @@ void WebPluginDelegateStub::OnPrint(base::SharedMemoryHandle* shared_memory, void WebPluginDelegateStub::OnUpdateGeometry( const gfx::Rect& window_rect, const gfx::Rect& clip_rect, - const TransportDIB::Id& windowless_buffer_id, - const TransportDIB::Id& background_buffer_id) { + const TransportDIB::Handle& windowless_buffer, + const TransportDIB::Handle& background_buffer) { webplugin_->UpdateGeometry( window_rect, clip_rect, - windowless_buffer_id, background_buffer_id); + windowless_buffer, background_buffer); } void WebPluginDelegateStub::OnGetPluginScriptableObject(int* route_id, diff --git a/chrome/plugin/webplugin_delegate_stub.h b/chrome/plugin/webplugin_delegate_stub.h index 10ac7a4..15b88a8 100644 --- a/chrome/plugin/webplugin_delegate_stub.h +++ b/chrome/plugin/webplugin_delegate_stub.h @@ -71,8 +71,8 @@ class WebPluginDelegateStub : public IPC::Channel::Listener, void OnUpdateGeometry(const gfx::Rect& window_rect, const gfx::Rect& clip_rect, - const TransportDIB::Id& windowless_buffer, - const TransportDIB::Id& background_buffer); + const TransportDIB::Handle& windowless_buffer, + const TransportDIB::Handle& background_buffer); void OnGetPluginScriptableObject(int* route_id, intptr_t* npobject_ptr); void OnSendJavaScriptStream(const std::string& url, const std::wstring& result, diff --git a/chrome/plugin/webplugin_proxy.cc b/chrome/plugin/webplugin_proxy.cc index b2fe778..94f2b2f 100644 --- a/chrome/plugin/webplugin_proxy.cc +++ b/chrome/plugin/webplugin_proxy.cc @@ -369,11 +369,16 @@ void WebPluginProxy::Paint(const gfx::Rect& rect) { #if defined(OS_WIN) if (!windowless_hdc_) return; +#elif defined(OS_MACOSX) + if (!windowless_context_.get()) + return; +#endif // Clear the damaged area so that if the plugin doesn't paint there we won't // end up with the old values. gfx::Rect offset_rect = rect; offset_rect.Offset(delegate_->GetRect().origin()); +#if defined(OS_WIN) if (!background_hdc_) { FillRect(windowless_hdc_, &offset_rect.ToRECT(), static_cast(GetStockObject(BLACK_BRUSH))); @@ -393,6 +398,22 @@ void WebPluginProxy::Paint(const gfx::Rect& rect) { SelectClipRgn(windowless_hdc_, NULL); DeleteObject(clip_region); +#elif defined(OS_MACOSX) + CGContextSaveGState(windowless_context_); + if (!background_context_.get()) { + CGContextSetFillColorWithColor(windowless_context_, + CGColorGetConstantColor(kCGColorWhite)); + CGContextFillRect(windowless_context_, rect.ToCGRect()); + } else { + scoped_cftyperef image( + CGBitmapContextCreateImage(background_context_)); + scoped_cftyperef sub_image( + CGImageCreateWithImageInRect(image, rect.ToCGRect())); + CGContextDrawImage(background_context_, rect.ToCGRect(), sub_image); + } + CGContextClipToRect(windowless_context_, rect.ToCGRect()); + delegate_->Paint(windowless_context_, rect); + CGContextRestoreGState(windowless_context_); #else // TODO(port): windowless painting. NOTIMPLEMENTED(); @@ -402,22 +423,21 @@ void WebPluginProxy::Paint(const gfx::Rect& rect) { void WebPluginProxy::UpdateGeometry( const gfx::Rect& window_rect, const gfx::Rect& clip_rect, - const TransportDIB::Id& windowless_buffer_id, - const TransportDIB::Id& background_buffer_id) { - // TODO(port): this isn't correct usage of a TransportDIB; for now, - // the caller temporarly just stuffs the handle into the HANDLE - // field of the TransportDIB::Id so it should behave like the older - // code. + const TransportDIB::Handle& windowless_buffer, + const TransportDIB::Handle& background_buffer) { gfx::Rect old = delegate_->GetRect(); gfx::Rect old_clip_rect = delegate_->GetClipRect(); delegate_->UpdateGeometry(window_rect, clip_rect); -#if defined(OS_WIN) bool moved = old.x() != window_rect.x() || old.y() != window_rect.y(); - if (windowless_buffer_id.handle) { +#if defined(OS_MACOSX) + if (windowless_buffer.fd > 0) { +#else + if (windowless_buffer) { +#endif // The plugin's rect changed, so now we have a new buffer to draw into. - SetWindowlessBuffer(windowless_buffer_id.handle, - background_buffer_id.handle); + SetWindowlessBuffer(windowless_buffer, + background_buffer); } else if (moved) { // The plugin moved, so update our world transform. UpdateTransform(); @@ -428,15 +448,12 @@ void WebPluginProxy::UpdateGeometry( old_clip_rect.IsEmpty() && !damaged_rect_.IsEmpty()) { InvalidateRect(damaged_rect_); } -#else - NOTIMPLEMENTED(); -#endif } #if defined(OS_WIN) void WebPluginProxy::SetWindowlessBuffer( - const base::SharedMemoryHandle& windowless_buffer, - const base::SharedMemoryHandle& background_buffer) { + const TransportDIB::Handle& windowless_buffer, + const TransportDIB::Handle& background_buffer) { // Convert the shared memory handle to a handle that works in our process, // and then use that to create an HDC. ConvertBuffer(windowless_buffer, @@ -501,6 +518,54 @@ void WebPluginProxy::UpdateTransform() { xf.eM22 = 1; SetWorldTransform(windowless_hdc_, &xf); } +#elif defined(OS_MACOSX) +void WebPluginProxy::UpdateTransform() { + NOTIMPLEMENTED(); +} + +void WebPluginProxy::SetWindowlessBuffer( + const TransportDIB::Handle& windowless_buffer, + const TransportDIB::Handle& background_buffer) { + // Convert the shared memory handle to a handle that works in our process, + // and then use that to create a CGContextRef. + windowless_dib_.reset(TransportDIB::Map(windowless_buffer)); + background_dib_.reset(TransportDIB::Map(background_buffer)); + scoped_cftyperef rgb_colorspace( + CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB)); + windowless_context_.reset(CGBitmapContextCreate( + windowless_dib_->memory(), + delegate_->GetRect().width(), + delegate_->GetRect().height(), + 8, 4 * delegate_->GetRect().width(), + rgb_colorspace, + kCGImageAlphaPremultipliedFirst | + kCGBitmapByteOrder32Host)); + CGContextTranslateCTM(windowless_context_, 0, delegate_->GetRect().height()); + CGContextScaleCTM(windowless_context_, 1, -1); + if (background_dib_.get()) { + background_context_.reset(CGBitmapContextCreate( + background_dib_->memory(), + delegate_->GetRect().width(), + delegate_->GetRect().height(), + 8, 4 * delegate_->GetRect().width(), + rgb_colorspace, + kCGImageAlphaPremultipliedFirst | + kCGBitmapByteOrder32Host)); + CGContextTranslateCTM(background_context_, 0, + delegate_->GetRect().height()); + CGContextScaleCTM(background_context_, 1, -1); + } +} +#elif defined (OS_LINUX) +void WebPluginProxy::UpdateTransform() { + NOTIMPLEMENTED(); +} + +void WebPluginProxy::SetWindowlessBuffer( + const TransportDIB::Handle& windowless_buffer, + const TransportDIB::Handle& background_buffer) { + NOTIMPLEMENTED(); +} #endif void WebPluginProxy::CancelDocumentLoad() { diff --git a/chrome/plugin/webplugin_proxy.h b/chrome/plugin/webplugin_proxy.h index 103be5c..a55a3da2 100644 --- a/chrome/plugin/webplugin_proxy.h +++ b/chrome/plugin/webplugin_proxy.h @@ -9,6 +9,9 @@ #include "base/hash_tables.h" #include "base/ref_counted.h" +#if defined(OS_MACOSX) +#include "base/scoped_cftyperef.h" +#endif #include "base/scoped_handle.h" #include "base/scoped_ptr.h" #include "base/shared_memory.h" @@ -104,8 +107,8 @@ class WebPluginProxy : public WebPlugin { void UpdateGeometry(const gfx::Rect& window_rect, const gfx::Rect& clip_rect, - const TransportDIB::Id& windowless_buffer, - const TransportDIB::Id& background_buffer); + const TransportDIB::Handle& windowless_buffer, + const TransportDIB::Handle& background_buffer); void CancelDocumentLoad(); @@ -129,22 +132,22 @@ class WebPluginProxy : public WebPlugin { // Handler for sending over the paint event to the plugin. void OnPaint(const gfx::Rect& damaged_rect); -#if defined(OS_WIN) // Updates the shared memory section where windowless plugins paint. - void SetWindowlessBuffer(const base::SharedMemoryHandle& windowless_buffer, - const base::SharedMemoryHandle& background_buffer); + void SetWindowlessBuffer(const TransportDIB::Handle& windowless_buffer, + const TransportDIB::Handle& background_buffer); +#if defined(OS_WIN) // Converts a shared memory section handle from the renderer process into a // bitmap and hdc that are mapped to this process. void ConvertBuffer(const base::SharedMemoryHandle& buffer, ScopedHandle* shared_section, ScopedBitmap* bitmap, ScopedHDC* hdc); +#endif // Called when a plugin's origin moves, so that we can update the world // transform of the local HDC. void UpdateTransform(); -#endif typedef base::hash_map ResourceClientMap; ResourceClientMap resource_clients_; @@ -174,6 +177,11 @@ class WebPluginProxy : public WebPlugin { ScopedHandle background_shared_section_; ScopedBitmap background_bitmap_; ScopedHDC background_hdc_; +#elif defined(OS_MACOSX) + scoped_ptr windowless_dib_; + scoped_ptr background_dib_; + scoped_cftyperef windowless_context_; + scoped_cftyperef background_context_; #endif ScopedRunnableMethodFactory runnable_method_factory_; diff --git a/chrome/renderer/render_view.cc b/chrome/renderer/render_view.cc index 5000ec1..7bd7c28 100644 --- a/chrome/renderer/render_view.cc +++ b/chrome/renderer/render_view.cc @@ -1762,7 +1762,6 @@ WebPluginDelegate* RenderView::CreatePluginDelegate( const std::string& mime_type, const std::string& clsid, std::string* actual_mime_type) { -#if defined(OS_WIN) || defined(OS_LINUX) if (!PluginChannelHost::IsListening()) return NULL; @@ -1783,13 +1782,16 @@ WebPluginDelegate* RenderView::CreatePluginDelegate( else mime_type_to_use = mime_type; -#if !defined(OS_LINUX) // In-proc plugins aren't supported on Linux. if (RenderProcess::current()->in_process_plugins()) { +#if defined(OS_WIN) // In-proc plugins aren't supported on Linux or Mac. return WebPluginDelegate::Create(path, mime_type_to_use, gfx::NativeViewFromId(host_window_)); - } +#else + NOTIMPLEMENTED(); + return NULL; #endif + } WebPluginDelegateProxy* proxy = WebPluginDelegateProxy::Create(url, mime_type_to_use, clsid, this); @@ -1799,11 +1801,6 @@ WebPluginDelegate* RenderView::CreatePluginDelegate( plugin_delegates_.push_back(proxy); return proxy; -#else - // TODO(port): Plugins currently not supported - NOTIMPLEMENTED(); - return NULL; -#endif } WebKit::WebMediaPlayer* RenderView::CreateWebMediaPlayer( diff --git a/chrome/renderer/webplugin_delegate_proxy.cc b/chrome/renderer/webplugin_delegate_proxy.cc index e07368d..c69adb9 100644 --- a/chrome/renderer/webplugin_delegate_proxy.cc +++ b/chrome/renderer/webplugin_delegate_proxy.cc @@ -13,6 +13,7 @@ #include "app/gfx/canvas.h" #include "app/l10n_util.h" #include "app/resource_bundle.h" +#include "base/file_util.h" #include "base/logging.h" #include "base/ref_counted.h" #include "base/string_util.h" @@ -43,6 +44,10 @@ #include "chrome/common/ipc_channel_posix.h" #endif +#if defined(OS_MACOSX) +#include "base/scoped_cftyperef.h" +#endif + using WebKit::WebCursorInfo; using WebKit::WebInputEvent; using WebKit::WebDragData; @@ -389,17 +394,13 @@ void WebPluginDelegateProxy::UpdateGeometry( const gfx::Rect& clip_rect) { plugin_rect_ = window_rect; - // Be careful to explicitly call the default constructors for these ids, - // as they can be POD on some platforms and we want them initialized. - TransportDIB::Id transport_store_id = TransportDIB::Id(); - TransportDIB::Id background_store_id = TransportDIB::Id(); + bool bitmaps_changed = false; if (windowless_) { -#if defined(OS_WIN) - // TODO(port): use TransportDIB instead of allocating these directly. if (!backing_store_canvas_.get() || (window_rect.width() != backing_store_canvas_->getDevice()->width() || window_rect.height() != backing_store_canvas_->getDevice()->height())) { + bitmaps_changed = true; // Create a shared memory section that the plugin paints into // asynchronously. ResetWindowlessBitmaps(); @@ -412,36 +413,52 @@ void WebPluginDelegateProxy::UpdateGeometry( ResetWindowlessBitmaps(); return; } - - // TODO(port): once we use TransportDIB we will properly fill in these - // ids; for now we just fill in the HANDLE field. - transport_store_id.handle = transport_store_->handle(); - if (background_store_.get()) - background_store_id.handle = background_store_->handle(); } } -#else - // TODO(port): refactor our allocation of backing stores. - NOTIMPLEMENTED(); + } + + IPC::Message* msg = NULL; +#if defined(OS_POSIX) + // If we're using POSIX mmap'd TransportDIBs, sending the handle across + // IPC establishes a new mapping rather than just sending a window ID, + // so only do so if we've actually recreated the shared memory bitmaps. + if (!bitmaps_changed) { + msg = new PluginMsg_UpdateGeometry(instance_id_, window_rect, clip_rect, + TransportDIB::Handle(), TransportDIB::Handle()); + } else #endif + if (transport_store_.get() && background_store_.get()) { + msg = new PluginMsg_UpdateGeometry(instance_id_, window_rect, clip_rect, + transport_store_->handle(), background_store_->handle()); + } else if (transport_store_.get()) { + msg = new PluginMsg_UpdateGeometry(instance_id_, window_rect, clip_rect, + transport_store_->handle(), TransportDIB::Handle()); + } else { + msg = new PluginMsg_UpdateGeometry(instance_id_, window_rect, clip_rect, + TransportDIB::Handle(), TransportDIB::Handle()); } - IPC::Message* msg = new PluginMsg_UpdateGeometry( - instance_id_, window_rect, clip_rect, - transport_store_id, background_store_id); msg->set_unblock(true); Send(msg); } -#if defined(OS_WIN) -// Copied from render_widget.cc -static size_t GetPaintBufSize(const gfx::Rect& rect) { - // TODO(darin): protect against overflow - return 4 * rect.width() * rect.height(); +#if defined(OS_MACOSX) +static void ReleaseTransportDIB(TransportDIB *dib) { + if (dib) { + IPC::Message* msg = new ViewHostMsg_FreeTransportDIB(dib->id()); + RenderThread::current()->Send(msg); + } } #endif void WebPluginDelegateProxy::ResetWindowlessBitmaps() { +#if defined(OS_MACOSX) + // tell the browser to relase these TransportDIBs + ReleaseTransportDIB(backing_store_.get()); + ReleaseTransportDIB(transport_store_.get()); + ReleaseTransportDIB(background_store_.get()); +#endif + backing_store_.reset(); transport_store_.reset(); backing_store_canvas_.reset(); @@ -452,28 +469,36 @@ void WebPluginDelegateProxy::ResetWindowlessBitmaps() { } bool WebPluginDelegateProxy::CreateBitmap( - scoped_ptr* memory, + scoped_ptr* memory, scoped_ptr* canvas) { -#if defined(OS_WIN) - size_t size = GetPaintBufSize(plugin_rect_); - scoped_ptr new_shared_memory(new base::SharedMemory()); - if (!new_shared_memory->Create(L"", false, true, size)) + int width = plugin_rect_.width(); + int height = plugin_rect_.height(); + const size_t stride = skia::PlatformCanvas::StrideForWidth(width); + const size_t size = stride * height; +#if defined(OS_LINUX) + static unsigned long max_size = 0; + if (max_size == 0) { + std::string contents; + file_util::ReadFileToString(FilePath("/proc/sys/kernel/shmmax"), &contents); + max_size = strtoul(contents.c_str(), NULL, 0); + } + if (size > max_size) return false; - - scoped_ptr new_canvas(new skia::PlatformCanvas); - if (!new_canvas->initialize(plugin_rect_.width(), plugin_rect_.height(), - true, new_shared_memory->handle())) { +#endif +#if defined(OS_MACOSX) + TransportDIB::Handle handle; + IPC::Message* msg = new ViewHostMsg_AllocTransportDIB(size, &handle); + if (!RenderThread::current()->Send(msg)) return false; - } - - memory->swap(new_shared_memory); - canvas->swap(new_canvas); - return true; + if (handle.fd < 0) + return false; + memory->reset(TransportDIB::Map(handle)); #else - // TODO(port): use TransportDIB properly. - NOTIMPLEMENTED(); - return false; + static uint32 sequence_number = 0; + memory->reset(TransportDIB::Create(size, sequence_number++)); #endif + canvas->reset((*memory)->GetPlatformCanvas(width, height)); + return true; } void WebPluginDelegateProxy::Paint(gfx::NativeDrawingContext context, @@ -489,36 +514,59 @@ void WebPluginDelegateProxy::Paint(gfx::NativeDrawingContext context, if (!windowless_) return; - // TODO(port): side-stepping some windowless plugin code for now. -#if defined(OS_WIN) // We got a paint before the plugin's coordinates, so there's no buffer to // copy from. - if (!backing_store_canvas_.get()) + if (!backing_store_canvas_.get()) { return; + } // Limit the damaged rectangle to whatever is contained inside the plugin // rectangle, as that's the rectangle that we'll bitblt to the hdc. gfx::Rect rect = damaged_rect.Intersect(plugin_rect_); + gfx::Rect offset_rect = rect; + offset_rect.Offset(-plugin_rect_.x(), -plugin_rect_.y()); bool background_changed = false; if (background_store_canvas_.get() && BackgroundChanged(context, rect)) { background_changed = true; +#if defined(OS_WIN) HDC background_hdc = background_store_canvas_->getTopPlatformDevice().getBitmapDC(); - BitBlt(background_hdc, rect.x()-plugin_rect_.x(), rect.y()-plugin_rect_.y(), + BitBlt(background_hdc, offset_rect.x(), offset_rect.y(), rect.width(), rect.height(), context, rect.x(), rect.y(), SRCCOPY); +#elif defined(OS_MACOSX) + CGContextRef background_context = + background_store_canvas_->getTopPlatformDevice().GetBitmapContext(); + scoped_cftyperef + background_image(CGBitmapContextCreateImage(background_context)); + scoped_cftyperef sub_image( + CGImageCreateWithImageInRect(background_image, offset_rect.ToCGRect())); + CGContextDrawImage(context, rect.ToCGRect(), sub_image); +#else + NOTIMPLEMENTED(); +#endif } - gfx::Rect offset_rect = rect; - offset_rect.Offset(-plugin_rect_.x(), -plugin_rect_.y()); if (background_changed || !backing_store_painted_.Contains(offset_rect)) { Send(new PluginMsg_Paint(instance_id_, offset_rect)); CopyFromTransportToBacking(offset_rect); } +#if defined(OS_WIN) HDC backing_hdc = backing_store_canvas_->getTopPlatformDevice().getBitmapDC(); BitBlt(context, rect.x(), rect.y(), rect.width(), rect.height(), backing_hdc, - rect.x()-plugin_rect_.x(), rect.y()-plugin_rect_.y(), SRCCOPY); + offset_rect.x(), offset_rect.y(), SRCCOPY); +#elif defined(OS_MACOSX) + CGContextRef backing_context = + backing_store_canvas_->getTopPlatformDevice().GetBitmapContext(); + scoped_cftyperef + backing_image(CGBitmapContextCreateImage(backing_context)); + scoped_cftyperef sub_image( + CGImageCreateWithImageInRect(backing_image, offset_rect.ToCGRect())); + CGContextDrawImage(context, rect.ToCGRect(), sub_image); +#else + NOTIMPLEMENTED(); +#endif if (invalidate_pending_) { // Only send the PaintAck message if this paint is in response to an @@ -527,18 +575,12 @@ void WebPluginDelegateProxy::Paint(gfx::NativeDrawingContext context, invalidate_pending_ = false; Send(new PluginMsg_DidPaint(instance_id_)); } -#else - // TODO(port): windowless plugin paint handling goes here. - NOTIMPLEMENTED(); -#endif } -#if defined(OS_WIN) -// TODO(port): this should be portable; just avoiding windowless plugins for -// now. bool WebPluginDelegateProxy::BackgroundChanged( - HDC hdc, + gfx::NativeDrawingContext hdc, const gfx::Rect& rect) { +#if defined(OS_WIN) HBITMAP hbitmap = static_cast(GetCurrentObject(hdc, OBJ_BITMAP)); if (hbitmap == NULL) { NOTREACHED(); @@ -578,9 +620,11 @@ bool WebPluginDelegateProxy::BackgroundChanged( return true; } +#else + NOTIMPLEMENTED(); +#endif return false; } -#endif void WebPluginDelegateProxy::Print(gfx::NativeDrawingContext context) { base::SharedMemoryHandle shared_memory; @@ -860,7 +904,7 @@ void WebPluginDelegateProxy::OnGetCPBrowsingContext(uint32* context) { *context = render_view_ ? render_view_->GetCPBrowsingContext() : 0; } -void WebPluginDelegateProxy::PaintSadPlugin(gfx::NativeDrawingContext hdc, +void WebPluginDelegateProxy::PaintSadPlugin(gfx::NativeDrawingContext context, const gfx::Rect& rect) { const int width = plugin_rect_.width(); const int height = plugin_rect_.height(); @@ -886,7 +930,7 @@ void WebPluginDelegateProxy::PaintSadPlugin(gfx::NativeDrawingContext hdc, #if defined(OS_WIN) skia::PlatformDevice& device = canvas.getTopPlatformDevice(); - device.drawToHDC(hdc, plugin_rect_.x(), plugin_rect_.y(), NULL); + device.drawToHDC(context, plugin_rect_.x(), plugin_rect_.y(), NULL); #elif defined(OS_LINUX) // Though conceptually we've been handed a cairo_surface_t* and we // could've just hooked up the canvas to draw directly onto it, our @@ -895,33 +939,46 @@ void WebPluginDelegateProxy::PaintSadPlugin(gfx::NativeDrawingContext hdc, // TODO(evanm): revisit when we have printing hooked up, as that might // change our usage of cairo. skia::PlatformDevice& device = canvas.getTopPlatformDevice(); - cairo_t* cairo = cairo_create(hdc); + cairo_t* cairo = cairo_create(context); cairo_surface_t* source_surface = device.beginPlatformPaint(); cairo_set_source_surface(cairo, source_surface, plugin_rect_.x(), plugin_rect_.y()); cairo_paint(cairo); cairo_destroy(cairo); // We have no endPlatformPaint() on the Linux PlatformDevice. // The surface is owned by the device. +#elif defined(OS_MACOSX) + canvas.getTopPlatformDevice().DrawToContext( + context, plugin_rect_.x(), plugin_rect_.y(), NULL); #else NOTIMPLEMENTED(); #endif } void WebPluginDelegateProxy::CopyFromTransportToBacking(const gfx::Rect& rect) { - if (!backing_store_canvas_.get()) + if (!backing_store_canvas_.get()) { return; + } -#if defined(OS_WIN) // Copy the damaged rect from the transport bitmap to the backing store. +#if defined(OS_WIN) HDC backing = backing_store_canvas_->getTopPlatformDevice().getBitmapDC(); HDC transport = transport_store_canvas_->getTopPlatformDevice().getBitmapDC(); BitBlt(backing, rect.x(), rect.y(), rect.width(), rect.height(), transport, rect.x(), rect.y(), SRCCOPY); - backing_store_painted_ = backing_store_painted_.Union(rect); +#elif defined(OS_MACOSX) + gfx::NativeDrawingContext backing = + backing_store_canvas_->getTopPlatformDevice().GetBitmapContext(); + gfx::NativeDrawingContext transport = + transport_store_canvas_->getTopPlatformDevice().GetBitmapContext(); + scoped_cftyperef image(CGBitmapContextCreateImage(transport)); + scoped_cftyperef sub_image( + CGImageCreateWithImageInRect(image, rect.ToCGRect())); + CGContextDrawImage(backing, rect.ToCGRect(), sub_image); #else // TODO(port): probably some new code in TransportDIB should go here. NOTIMPLEMENTED(); #endif + backing_store_painted_ = backing_store_painted_.Union(rect); } void WebPluginDelegateProxy::OnHandleURLRequest( diff --git a/chrome/renderer/webplugin_delegate_proxy.h b/chrome/renderer/webplugin_delegate_proxy.h index f7b91e8..a772384 100644 --- a/chrome/renderer/webplugin_delegate_proxy.h +++ b/chrome/renderer/webplugin_delegate_proxy.h @@ -13,6 +13,7 @@ #include "base/gfx/native_widget_types.h" #include "base/ref_counted.h" #include "chrome/common/ipc_message.h" +#include "chrome/common/transport_dib.h" #include "chrome/renderer/plugin_channel_host.h" #include "googleurl/src/gurl.h" #include "skia/ext/platform_canvas.h" @@ -146,14 +147,10 @@ class WebPluginDelegateProxy : public WebPluginDelegate, // Draw a graphic indicating a crashed plugin. void PaintSadPlugin(gfx::NativeDrawingContext context, const gfx::Rect& rect); -#if defined(OS_WIN) - // Returns true if the given rectangle is different in the hdc and the - // current background bitmap. - bool BackgroundChanged(HDC hdc, const gfx::Rect& rect); -#else - // TODO(port): this should be portable; just avoiding windowless plugins for - // now. -#endif + // Returns true if the given rectangle is different in the native drawing + // context and the current background bitmap. + bool BackgroundChanged(gfx::NativeDrawingContext context, + const gfx::Rect& rect); // Copies the given rectangle from the transport bitmap to the backing store. void CopyFromTransportToBacking(const gfx::Rect& rect); @@ -162,7 +159,7 @@ class WebPluginDelegateProxy : public WebPluginDelegate, void ResetWindowlessBitmaps(); // Creates a shared memory section and canvas. - bool CreateBitmap(scoped_ptr* memory, + bool CreateBitmap(scoped_ptr* memory, scoped_ptr* canvas); RenderView* render_view_; @@ -195,11 +192,11 @@ class WebPluginDelegateProxy : public WebPluginDelegate, // store when we get an invalidate from it. The background bitmap is used // for transparent plugins, as they need the backgroud data during painting. bool transparent_; - scoped_ptr backing_store_; + scoped_ptr backing_store_; scoped_ptr backing_store_canvas_; - scoped_ptr transport_store_; + scoped_ptr transport_store_; scoped_ptr transport_store_canvas_; - scoped_ptr background_store_; + scoped_ptr background_store_; scoped_ptr background_store_canvas_; // This lets us know which portion of the backing store has been painted into. gfx::Rect backing_store_painted_; diff --git a/webkit/glue/plugins/plugin_host.cc b/webkit/glue/plugins/plugin_host.cc index 2e1c233..40b2e1e 100644 --- a/webkit/glue/plugins/plugin_host.cc +++ b/webkit/glue/plugins/plugin_host.cc @@ -600,22 +600,19 @@ void NPN_InvalidateRect(NPP id, NPRect *invalidRect) { DCHECK(plugin.get() != NULL); if (plugin.get() && plugin->webplugin()) { if (invalidRect) { - if (!plugin->windowless()) { #if defined(OS_WIN) + if (!plugin->windowless()) { RECT rect = {0}; rect.left = invalidRect->left; rect.right = invalidRect->right; rect.top = invalidRect->top; rect.bottom = invalidRect->bottom; ::InvalidateRect(plugin->window_handle(), &rect, FALSE); -#elif defined(OS_MACOSX) - NOTIMPLEMENTED(); -#else - NOTIMPLEMENTED(); -#endif return; } - +#elif defined(OS_LINUX) + NOTIMPLEMENTED(); +#endif gfx::Rect rect(invalidRect->left, invalidRect->top, invalidRect->right - invalidRect->left, @@ -633,8 +630,13 @@ void NPN_InvalidateRegion(NPP id, NPRegion invalidRegion) { // // Similar to NPN_InvalidateRect. - // TODO: implement me - DLOG(INFO) << "NPN_InvalidateRegion is not implemented yet."; + // TODO: this is overkill--add platform-specific region handling (at the + // very least, fetch the region's bounding box and pass it to InvalidateRect). + scoped_refptr plugin = FindInstance(id); + DCHECK(plugin.get() != NULL); + if (plugin.get() && plugin->webplugin()) { + plugin->webplugin()->Invalidate(); + } } void NPN_ForceRedraw(NPP id) { @@ -652,10 +654,8 @@ void NPN_ForceRedraw(NPP id) { // settings. The HDC settings must be restored whenever control returns // back to the browser, either before returning from NPP_HandleEvent or // before calling a drawing-related netscape method. - // - // TODO: implement me - DLOG(INFO) << "NPN_ForceRedraw is not implemented yet."; + NOTIMPLEMENTED(); } NPError NPN_GetValue(NPP id, NPNVariable variable, void *value) { diff --git a/webkit/glue/plugins/webplugin_delegate_impl.h b/webkit/glue/plugins/webplugin_delegate_impl.h index a6f0715..35302dc7 100644 --- a/webkit/glue/plugins/webplugin_delegate_impl.h +++ b/webkit/glue/plugins/webplugin_delegate_impl.h @@ -95,6 +95,7 @@ class WebPluginDelegateImpl : public WebPluginDelegate { NPAPI::PluginInstance *instance); ~WebPluginDelegateImpl(); +#if !defined(OS_MACOSX) //-------------------------- // used for windowed plugins void WindowedUpdateGeometry(const gfx::Rect& window_rect, @@ -115,6 +116,7 @@ class WebPluginDelegateImpl : public WebPluginDelegate { // Tells the plugin about the current state of the window. // See NPAPI NPP_SetWindow for more information. void WindowedSetWindow(); +#endif #if defined(OS_WIN) // Registers the window class for our window @@ -153,10 +155,10 @@ class WebPluginDelegateImpl : public WebPluginDelegate { // Closes down and destroys our plugin instance. void DestroyInstance(); +#if !defined(OS_MACOSX) // used for windowed plugins gfx::PluginWindowHandle windowed_handle_; bool windowed_did_set_window_; -#if defined(OS_WIN) gfx::Rect windowed_last_pos_; #endif diff --git a/webkit/glue/plugins/webplugin_delegate_impl_mac.mm b/webkit/glue/plugins/webplugin_delegate_impl_mac.mm index 414d635..9232fca 100644 --- a/webkit/glue/plugins/webplugin_delegate_impl_mac.mm +++ b/webkit/glue/plugins/webplugin_delegate_impl_mac.mm @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#import + #include "config.h" #include "webkit/glue/plugins/webplugin_delegate_impl.h" @@ -29,31 +31,27 @@ using WebKit::WebKeyboardEvent; using WebKit::WebInputEvent; using WebKit::WebMouseEvent; -namespace { +// Important implementation notes: The Mac definition of NPAPI, particularly +// the distinction between windowed and windowless modes, differs from the +// Windows and Linux definitions. Most of those differences are +// accomodated by the WebPluginDelegate class. -const wchar_t kWebPluginDelegateProperty[] = L"WebPluginDelegateProperty"; -const wchar_t kPluginNameAtomProperty[] = L"PluginNameAtom"; -const wchar_t kDummyActivationWindowName[] = L"DummyWindowForActivation"; -const wchar_t kPluginOrigProc[] = L"OriginalPtr"; -const wchar_t kPluginFlashThrottle[] = L"FlashThrottle"; +namespace { -// The fastest we are willing to process WM_USER+1 events for Flash. +// The fastest we are willing to process idle events for Flash. // Flash can easily exceed the limits of our CPU if we don't throttle it. // The throttle has been chosen by testing various delays and compromising // on acceptable Flash performance and reasonable CPU consumption. // -// I'd like to make the throttle delay variable, based on the amount of +// We'd like to make the throttle delay variable, based on the amount of // time currently required to paint Flash plugins. There isn't a good // way to count the time spent in aggregate plugin painting, however, so // this seems to work well enough. -const int kFlashWMUSERMessageThrottleDelayMs = 5; +const int kFlashIdleThrottleDelayMs = 20; // 20ms (50Hz) // The current instance of the plugin which entered the modal loop. WebPluginDelegateImpl* g_current_plugin_instance = NULL; -// base::LazyInstance > g_throttle_queue(base::LINKER_INITIALIZED); - - } // namespace WebPluginDelegate* WebPluginDelegate::Create( @@ -81,27 +79,20 @@ WebPluginDelegateImpl::WebPluginDelegateImpl( instance_(instance), quirks_(0), plugin_(NULL), - windowless_(false), - windowed_handle_(NULL), - windowed_did_set_window_(false), + // all Mac plugins are "windowless" in the Windows/X11 sense + windowless_(true), windowless_needs_set_window_(true), handle_event_depth_(0), user_gesture_message_posted_(this), user_gesture_msg_factory_(this) { memset(&window_, 0, sizeof(window_)); - - const WebPluginInfo& plugin_info = instance_->plugin_lib()->plugin_info(); - std::string filename = - StringToLowerASCII(plugin_info.path.BaseName().value()); - - // plugin_module_handle_ = ::GetModuleHandle(plugin_info.path.value().c_str()); } WebPluginDelegateImpl::~WebPluginDelegateImpl() { DestroyInstance(); - if (!windowless_) - WindowedDestroyWindow(); + if (cg_context_.window) + DisposeWindow(cg_context_.window); } void WebPluginDelegateImpl::PluginDestroyed() { @@ -128,20 +119,11 @@ bool WebPluginDelegateImpl::Initialize(const GURL& url, if (!start_result) return false; - windowless_ = instance_->windowless(); - if (windowless_) { - // For windowless plugins we should set the containing window handle - // as the instance window handle. This is what Safari does. Not having - // a valid window handle causes subtle bugs with plugins which retreive - // the window handle and validate the same. The window handle can be - // retreived via NPN_GetValue of NPNVnetscapeWindow. - // instance_->set_window_handle(parent_); - } else { - if (!WindowedCreatePlugin()) - return false; - } + cg_context_.window = NULL; + window_.window = &cg_context_; + window_.type = NPWindowTypeDrawable; - // plugin->SetWindow(windowed_handle_); + plugin->SetWindow(NULL); plugin_url_ = url.spec(); return true; @@ -154,18 +136,8 @@ void WebPluginDelegateImpl::DestroyInstance() { // 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); - - if (instance_->plugin_lib()) { - // Unpatch if this is the last plugin instance. - } - instance_ = 0; } } @@ -173,11 +145,36 @@ void WebPluginDelegateImpl::DestroyInstance() { void WebPluginDelegateImpl::UpdateGeometry( const gfx::Rect& window_rect, const gfx::Rect& clip_rect) { - if (windowless_) { - WindowlessUpdateGeometry(window_rect, clip_rect); - } else { - WindowedUpdateGeometry(window_rect, clip_rect); + + if (!window_rect.IsEmpty()) { + NSPoint windowOffset = {0.0, 0.0}; + Rect window_bounds; + window_bounds.top = window_rect.y() + windowOffset.y; + window_bounds.left = window_rect.x() + windowOffset.x; + window_bounds.bottom = window_rect.y() + window_rect.height(); + window_bounds.right = window_rect.x() + window_rect.width(); + + if (!cg_context_.window) { + // For all plugins we create a placeholder offscreen window for the use + // of NPWindow. NPAPI on the Mac requires a Carbon WindowRef for the + // "browser window", even if we're not using the Quickdraw drawing model. + // Not having a valid window reference causes subtle bugs with plugins + // which retreive the NPWindow and validate the same. The NPWindow + // can be retreived via NPN_GetValue of NPNVnetscapeWindow. + + WindowRef window_ref; + if (CreateNewWindow(kDocumentWindowClass, + kWindowStandardDocumentAttributes, + &window_bounds, + &window_ref) == noErr) { + cg_context_.window = window_ref; + } + } else { + SetWindowBounds(cg_context_.window, kWindowContentRgn, &window_bounds); + } } + + WindowlessUpdateGeometry(window_rect, clip_rect); } void WebPluginDelegateImpl::Paint(CGContextRef context, const gfx::Rect& rect) { @@ -218,12 +215,6 @@ void WebPluginDelegateImpl::SendJavaScriptStream(const std::string& url, void WebPluginDelegateImpl::DidReceiveManualResponse( const std::string& url, const std::string& mime_type, const std::string& headers, uint32 expected_length, uint32 last_modified) { - if (!windowless_) { - // Calling NPP_WriteReady before NPP_SetWindow causes movies to not load in - // Flash. See http://b/issue?id=892174. - DCHECK(windowed_did_set_window_); - } - instance()->DidReceiveManualResponse(url, mime_type, headers, expected_length, last_modified); } @@ -250,91 +241,6 @@ void WebPluginDelegateImpl::InstallMissingPlugin() { instance()->NPP_HandleEvent(&evt); } -void WebPluginDelegateImpl::WindowedUpdateGeometry( - const gfx::Rect& window_rect, - const gfx::Rect& clip_rect) { - if (WindowedReposition(window_rect, clip_rect) || - !windowed_did_set_window_) { - // Let the plugin know that it has been moved - WindowedSetWindow(); - } -} - -bool WebPluginDelegateImpl::WindowedCreatePlugin() { - DCHECK(!windowed_handle_); - - // create window - if (windowed_handle_ == 0) - return false; - - return true; -} - -void WebPluginDelegateImpl::WindowedDestroyWindow() { - if (windowed_handle_ != NULL) { - // destroy the window - windowed_handle_ = 0; - } -} - -bool WebPluginDelegateImpl::WindowedReposition( - const gfx::Rect& window_rect, - const gfx::Rect& clip_rect) { - if (!windowed_handle_) { - NOTREACHED(); - return false; - } - - if (window_rect_ == window_rect && clip_rect_ == clip_rect) - return false; - - // Clipping is handled by WebPlugin. - if (window_rect.size() != window_rect_.size()) { - // resize window - } - - window_rect_ = window_rect; - clip_rect_ = clip_rect; - - // Ensure that the entire window gets repainted. - // invalidate entire window - - return true; -} - -void WebPluginDelegateImpl::WindowedSetWindow() { - if (!instance_) - return; - - if (!windowed_handle_) { - NOTREACHED(); - return; - } - - // instance()->set_window_handle(windowed_handle_); - - DCHECK(!instance()->windowless()); - - 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(); - - cg_context_.context = NULL; - cg_context_.window = NULL; - window_.window = &cg_context_; - window_.type = NPWindowTypeDrawable; // NPWindowTypeWindow; - - // Reset this flag before entering the instance in case of side-effects. - windowed_did_set_window_ = true; - - NPError err = instance()->NPP_SetWindow(&window_); -} - void WebPluginDelegateImpl::WindowlessUpdateGeometry( const gfx::Rect& window_rect, const gfx::Rect& clip_rect) { @@ -349,20 +255,72 @@ void WebPluginDelegateImpl::WindowlessUpdateGeometry( if (window_rect_ != window_rect) { window_rect_ = window_rect; - WindowlessSetWindow(true); - - NPEvent pos_changed_event; - - instance()->NPP_HandleEvent(&pos_changed_event); + 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; + windowless_needs_set_window_ = true; } } -void WebPluginDelegateImpl::WindowlessPaint(gfx::NativeDrawingContext hdc, +void WebPluginDelegateImpl::WindowlessPaint(gfx::NativeDrawingContext context, const gfx::Rect& damage_rect) { static StatsRate plugin_paint("Plugin.Paint"); StatsScope scope(plugin_paint); + + // We save and restore the NSGraphicsContext state in case the plugin uses + // Cocoa drawing. + [NSGraphicsContext saveGraphicsState]; + [NSGraphicsContext setCurrentContext:[NSGraphicsContext + graphicsContextWithGraphicsPort:context + flipped:NO]]; + + CGContextSaveGState(context); + cg_context_.context = context; + window_.window = &cg_context_; + window_.type = NPWindowTypeDrawable; + if (windowless_needs_set_window_) { + Rect window_bounds; + window_bounds.top = window_rect_.y(); + window_bounds.left = window_rect_.x(); + window_bounds.bottom = window_rect_.y() + window_rect_.height(); + window_bounds.right = window_rect_.x() + window_rect_.width(); + if (!cg_context_.window) { + // For all plugins we create a placeholder offscreen window for the use + // of NPWindow. NPAPI on the Mac requires a Carbon WindowRef for the + // "browser window", even if we're not using the Quickdraw drawing model. + // Not having a valid window reference causes subtle bugs with plugins + // which retreive the NPWindow and validate the same. The NPWindow + // can be retreived via NPN_GetValue of NPNVnetscapeWindow. + + WindowRef window_ref; + if (CreateNewWindow(kDocumentWindowClass, + kWindowStandardDocumentAttributes, + &window_bounds, + &window_ref) == noErr) { + cg_context_.window = window_ref; + } + } else { + SetWindowBounds(cg_context_.window, kWindowContentRgn, &window_bounds); + } + instance()->NPP_SetWindow(&window_); + windowless_needs_set_window_ = false; + } NPEvent paint_event; + paint_event.what = updateEvt; + paint_event.message = reinterpret_cast(cg_context_.window); + paint_event.when = TickCount(); + paint_event.where.h = 0; + paint_event.where.v = 0; + paint_event.modifiers = 0; instance()->NPP_HandleEvent(&paint_event); + CGContextRestoreGState(context); + [NSGraphicsContext restoreGraphicsState]; } void WebPluginDelegateImpl::WindowlessSetWindow(bool force_set_window) { @@ -372,8 +330,6 @@ void WebPluginDelegateImpl::WindowlessSetWindow(bool force_set_window) { if (window_rect_.IsEmpty()) // wait for geometry to be set. return; - DCHECK(instance()->windowless()); - window_.clipRect.top = clip_rect_.y(); window_.clipRect.left = clip_rect_.x(); window_.clipRect.bottom = clip_rect_.y() + clip_rect_.height(); @@ -388,25 +344,136 @@ void WebPluginDelegateImpl::WindowlessSetWindow(bool force_set_window) { // Reset this flag before entering the instance in case of side-effects. windowless_needs_set_window_ = false; + Rect window_bounds; + window_bounds.top = window_rect_.y(); + window_bounds.left = window_rect_.x(); + window_bounds.bottom = window_rect_.y() + window_rect_.height(); + window_bounds.right = window_rect_.x() + window_rect_.width(); + if (!cg_context_.window) { + // For all plugins we create a placeholder offscreen window for the use + // of NPWindow. NPAPI on the Mac requires a Carbon WindowRef for the + // "browser window", even if we're not using the Quickdraw drawing model. + // Not having a valid window reference causes subtle bugs with plugins + // which retreive the NPWindow and validate the same. The NPWindow + // can be retreived via NPN_GetValue of NPNVnetscapeWindow. + + WindowRef window_ref; + if (CreateNewWindow(kDocumentWindowClass, + kWindowStandardDocumentAttributes, + &window_bounds, + &window_ref) == noErr) { + cg_context_.window = window_ref; + } + } else { + SetWindowBounds(cg_context_.window, kWindowContentRgn, &window_bounds); + } + NPError err = instance()->NPP_SetWindow(&window_); DCHECK(err == NPERR_NO_ERROR); } void WebPluginDelegateImpl::SetFocus() { - DCHECK(instance()->windowless()); + NPEvent focus_event = { 0 }; + focus_event.what = NPEventType_GetFocusEvent; + focus_event.when = TickCount(); + instance()->NPP_HandleEvent(&focus_event); +} - NPEvent focus_event; +static bool NPEventFromWebMouseEvent(const WebMouseEvent& event, + NPEvent *np_event) { + np_event->where.h = event.windowX; + np_event->where.v = event.windowY; + + if (event.modifiers & WebInputEvent::ControlKey) + np_event->modifiers |= controlKey; + if (event.modifiers & WebInputEvent::ShiftKey) + np_event->modifiers |= shiftKey; + + switch (event.type) { + case WebInputEvent::MouseMove: + case WebInputEvent::MouseLeave: + case WebInputEvent::MouseEnter: + np_event->what = NPEventType_AdjustCursorEvent; + return true; + case WebInputEvent::MouseDown: + switch (event.button) { + case WebMouseEvent::ButtonLeft: + case WebMouseEvent::ButtonMiddle: + case WebMouseEvent::ButtonRight: + np_event->what = mouseDown; + break; + } + return true; + case WebInputEvent::MouseUp: + switch (event.button) { + case WebMouseEvent::ButtonLeft: + case WebMouseEvent::ButtonMiddle: + case WebMouseEvent::ButtonRight: + np_event->what = mouseUp; + break; + } + return true; + default: + NOTREACHED(); + return false; + } +} - instance()->NPP_HandleEvent(&focus_event); +static bool NPEventFromWebKeyboardEvent(const WebKeyboardEvent& event, + NPEvent *np_event) { + np_event->message = event.nativeKeyCode; + + switch (event.type) { + case WebInputEvent::KeyDown: + np_event->what = keyDown; + return true; + case WebInputEvent::KeyUp: + np_event->what = keyUp; + return true; + default: + NOTREACHED(); + return false; + } +} + +static bool NPEventFromWebInputEvent(const WebInputEvent& event, + NPEvent* np_event) { + switch (event.type) { + case WebInputEvent::MouseMove: + case WebInputEvent::MouseLeave: + case WebInputEvent::MouseEnter: + case WebInputEvent::MouseDown: + case WebInputEvent::MouseUp: + if (event.size < sizeof(WebMouseEvent)) { + NOTREACHED(); + return false; + } + return NPEventFromWebMouseEvent(*static_cast(&event), np_event); + case WebInputEvent::KeyDown: + case WebInputEvent::KeyUp: + if (event.size < sizeof(WebKeyboardEvent)) { + NOTREACHED(); + return false; + } + return NPEventFromWebKeyboardEvent(*static_cast(&event), np_event); + default: + DLOG(WARNING) << "unknown event type" << event.type; + return false; + } } bool WebPluginDelegateImpl::HandleInputEvent(const WebInputEvent& event, WebCursorInfo* cursor) { DCHECK(windowless_) << "events should only be received in windowless mode"; DCHECK(cursor != NULL); - // TODO: convert event into a NPEvent, and call NPP_HandleEvent(np_event). - return true; + NPEvent np_event = {0}; + if (!NPEventFromWebInputEvent(event, &np_event)) { + return false; + } + np_event.when = TickCount(); + bool ret = instance()->NPP_HandleEvent(&np_event) != 0; + return ret; } WebPluginResourceClient* WebPluginDelegateImpl::CreateResourceClient( -- cgit v1.1