| Index: webkit/glue/plugins/webplugin_delegate_impl_mac.mm
|
| ===================================================================
|
| --- webkit/glue/plugins/webplugin_delegate_impl_mac.mm (revision 20445)
|
| +++ webkit/glue/plugins/webplugin_delegate_impl_mac.mm (working copy)
|
| @@ -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 <Cocoa/Cocoa.h>
|
| +
|
| #include "config.h"
|
| #include "webkit/glue/plugins/webplugin_delegate_impl.h"
|
|
|
| @@ -29,31 +31,27 @@
|
| using WebKit::WebInputEvent;
|
| using WebKit::WebMouseEvent;
|
|
|
| +// 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.
|
| +
|
| namespace {
|
|
|
| -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";
|
| -
|
| -// 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<std::list<MSG> > g_throttle_queue(base::LINKER_INITIALIZED);
|
| -
|
| -
|
| } // namespace
|
|
|
| WebPluginDelegate* WebPluginDelegate::Create(
|
| @@ -81,27 +79,20 @@
|
| 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 @@
|
| 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 @@
|
| // 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::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::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 @@
|
| 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 @@
|
| 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<StatsRate> 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<uint32>(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 @@
|
| 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 @@
|
| // 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;
|
|
|
| - instance()->NPP_HandleEvent(&focus_event);
|
| + 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;
|
| + }
|
| }
|
|
|
| +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<const WebMouseEvent*>(&event), np_event);
|
| + case WebInputEvent::KeyDown:
|
| + case WebInputEvent::KeyUp:
|
| + if (event.size < sizeof(WebKeyboardEvent)) {
|
| + NOTREACHED();
|
| + return false;
|
| + }
|
| + return NPEventFromWebKeyboardEvent(*static_cast<const WebKeyboardEvent*>(&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(
|
|
|