--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/WebKit/mac/Plugins/WebNetscapePluginEventHandlerCarbon.mm Fri Sep 17 09:02:29 2010 +0300
@@ -0,0 +1,434 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if ENABLE(NETSCAPE_PLUGIN_API) && !defined(__LP64__)
+
+#import "WebNetscapePluginEventHandlerCarbon.h"
+
+#import "WebNetscapePluginView.h"
+#import "WebKitLogging.h"
+#import "WebKitSystemInterface.h"
+
+// Send null events 50 times a second when active, so plug-ins like Flash get high frame rates.
+#define NullEventIntervalActive 0.02
+#define NullEventIntervalNotActive 0.25
+
+WebNetscapePluginEventHandlerCarbon::WebNetscapePluginEventHandlerCarbon(WebNetscapePluginView* pluginView)
+ : WebNetscapePluginEventHandler(pluginView)
+ , m_keyEventHandler(0)
+ , m_suspendKeyUpEvents(false)
+{
+}
+
+static void getCarbonEvent(EventRecord* carbonEvent)
+{
+ carbonEvent->what = nullEvent;
+ carbonEvent->message = 0;
+ carbonEvent->when = TickCount();
+
+ GetGlobalMouse(&carbonEvent->where);
+ carbonEvent->where.h = static_cast<short>(carbonEvent->where.h * HIGetScaleFactor());
+ carbonEvent->where.v = static_cast<short>(carbonEvent->where.v * HIGetScaleFactor());
+ carbonEvent->modifiers = GetCurrentKeyModifiers();
+ if (!Button())
+ carbonEvent->modifiers |= btnState;
+}
+
+static EventModifiers modifiersForEvent(NSEvent *event)
+{
+ EventModifiers modifiers;
+ unsigned int modifierFlags = [event modifierFlags];
+ NSEventType eventType = [event type];
+
+ modifiers = 0;
+
+ if (eventType != NSLeftMouseDown && eventType != NSRightMouseDown)
+ modifiers |= btnState;
+
+ if (modifierFlags & NSCommandKeyMask)
+ modifiers |= cmdKey;
+
+ if (modifierFlags & NSShiftKeyMask)
+ modifiers |= shiftKey;
+
+ if (modifierFlags & NSAlphaShiftKeyMask)
+ modifiers |= alphaLock;
+
+ if (modifierFlags & NSAlternateKeyMask)
+ modifiers |= optionKey;
+
+ if (modifierFlags & NSControlKeyMask || eventType == NSRightMouseDown)
+ modifiers |= controlKey;
+
+ return modifiers;
+}
+
+static void getCarbonEvent(EventRecord *carbonEvent, NSEvent *cocoaEvent)
+{
+ if (WKConvertNSEventToCarbonEvent(carbonEvent, cocoaEvent)) {
+ carbonEvent->where.h = static_cast<short>(carbonEvent->where.h * HIGetScaleFactor());
+ carbonEvent->where.v = static_cast<short>(carbonEvent->where.v * HIGetScaleFactor());
+ return;
+ }
+
+ NSPoint where = [[cocoaEvent window] convertBaseToScreen:[cocoaEvent locationInWindow]];
+
+ carbonEvent->what = nullEvent;
+ carbonEvent->message = 0;
+ carbonEvent->when = (UInt32)([cocoaEvent timestamp] * 60); // seconds to ticks
+ carbonEvent->where.h = (short)where.x;
+ carbonEvent->where.v = (short)(NSMaxY([[[NSScreen screens] objectAtIndex:0] frame]) - where.y);
+ carbonEvent->modifiers = modifiersForEvent(cocoaEvent);
+}
+
+void WebNetscapePluginEventHandlerCarbon::sendNullEvent()
+{
+ EventRecord event;
+
+ getCarbonEvent(&event);
+
+ // Plug-in should not react to cursor position when not active or when a menu is down.
+ MenuTrackingData trackingData;
+ OSStatus error = GetMenuTrackingData(NULL, &trackingData);
+
+ // Plug-in should not react to cursor position when the actual window is not key.
+ if (![[m_pluginView window] isKeyWindow] || (error == noErr && trackingData.menu)) {
+ // FIXME: Does passing a v and h of -1 really prevent it from reacting to the cursor position?
+ event.where.v = -1;
+ event.where.h = -1;
+ }
+
+ sendEvent(&event);
+}
+
+void WebNetscapePluginEventHandlerCarbon::drawRect(CGContextRef, const NSRect&)
+{
+ EventRecord event;
+
+ getCarbonEvent(&event);
+ event.what = updateEvt;
+ WindowRef windowRef = (WindowRef)[[m_pluginView window] windowRef];
+ event.message = (unsigned long)windowRef;
+
+ BOOL acceptedEvent;
+ acceptedEvent = sendEvent(&event);
+
+ LOG(PluginEvents, "NPP_HandleEvent(updateEvt): %d", acceptedEvent);
+}
+
+void WebNetscapePluginEventHandlerCarbon::mouseDown(NSEvent* theEvent)
+{
+ EventRecord event;
+
+ getCarbonEvent(&event, theEvent);
+ event.what = ::mouseDown;
+
+ BOOL acceptedEvent;
+ acceptedEvent = sendEvent(&event);
+
+ LOG(PluginEvents, "NPP_HandleEvent(mouseDown): %d pt.v=%d, pt.h=%d", acceptedEvent, event.where.v, event.where.h);
+}
+
+void WebNetscapePluginEventHandlerCarbon::mouseUp(NSEvent* theEvent)
+{
+ EventRecord event;
+
+ getCarbonEvent(&event, theEvent);
+ event.what = ::mouseUp;
+
+ BOOL acceptedEvent;
+ acceptedEvent = sendEvent(&event);
+
+ LOG(PluginEvents, "NPP_HandleEvent(mouseUp): %d pt.v=%d, pt.h=%d", acceptedEvent, event.where.v, event.where.h);
+}
+
+bool WebNetscapePluginEventHandlerCarbon::scrollWheel(NSEvent* theEvent)
+{
+ return false;
+}
+
+void WebNetscapePluginEventHandlerCarbon::mouseEntered(NSEvent* theEvent)
+{
+ EventRecord event;
+
+ getCarbonEvent(&event, theEvent);
+ event.what = adjustCursorEvent;
+
+ BOOL acceptedEvent;
+ acceptedEvent = sendEvent(&event);
+
+ LOG(PluginEvents, "NPP_HandleEvent(mouseEntered): %d", acceptedEvent);
+}
+
+void WebNetscapePluginEventHandlerCarbon::mouseExited(NSEvent* theEvent)
+{
+ EventRecord event;
+
+ getCarbonEvent(&event, theEvent);
+ event.what = adjustCursorEvent;
+
+ BOOL acceptedEvent;
+ acceptedEvent = sendEvent(&event);
+
+ LOG(PluginEvents, "NPP_HandleEvent(mouseExited): %d", acceptedEvent);
+}
+
+void WebNetscapePluginEventHandlerCarbon::mouseDragged(NSEvent*)
+{
+}
+
+void WebNetscapePluginEventHandlerCarbon::mouseMoved(NSEvent* theEvent)
+{
+ EventRecord event;
+
+ getCarbonEvent(&event, theEvent);
+ event.what = adjustCursorEvent;
+
+ BOOL acceptedEvent;
+ acceptedEvent = sendEvent(&event);
+
+ LOG(PluginEvents, "NPP_HandleEvent(mouseMoved): %d", acceptedEvent);
+}
+
+void WebNetscapePluginEventHandlerCarbon::keyDown(NSEvent *theEvent)
+{
+ m_suspendKeyUpEvents = true;
+ WKSendKeyEventToTSM(theEvent);
+}
+
+void WebNetscapePluginEventHandlerCarbon::syntheticKeyDownWithCommandModifier(int keyCode, char character)
+{
+ EventRecord event;
+ getCarbonEvent(&event);
+
+ event.what = ::keyDown;
+ event.modifiers |= cmdKey;
+ event.message = keyCode << 8 | character;
+ sendEvent(&event);
+}
+
+static UInt32 keyMessageForEvent(NSEvent *event)
+{
+ NSData *data = [[event characters] dataUsingEncoding:CFStringConvertEncodingToNSStringEncoding(CFStringGetSystemEncoding())];
+ if (!data)
+ return 0;
+
+ UInt8 characterCode;
+ [data getBytes:&characterCode length:1];
+ UInt16 keyCode = [event keyCode];
+ return keyCode << 8 | characterCode;
+}
+
+void WebNetscapePluginEventHandlerCarbon::keyUp(NSEvent* theEvent)
+{
+ WKSendKeyEventToTSM(theEvent);
+
+ // TSM won't send keyUp events so we have to send them ourselves.
+ // Only send keyUp events after we receive the TSM callback because this is what plug-in expect from OS 9.
+ if (!m_suspendKeyUpEvents) {
+ EventRecord event;
+
+ getCarbonEvent(&event, theEvent);
+ event.what = ::keyUp;
+
+ if (event.message == 0)
+ event.message = keyMessageForEvent(theEvent);
+
+ sendEvent(&event);
+ }
+}
+
+void WebNetscapePluginEventHandlerCarbon::flagsChanged(NSEvent*)
+{
+}
+
+void WebNetscapePluginEventHandlerCarbon::focusChanged(bool hasFocus)
+{
+ EventRecord event;
+
+ getCarbonEvent(&event);
+ bool acceptedEvent;
+ if (hasFocus) {
+ event.what = getFocusEvent;
+ acceptedEvent = sendEvent(&event);
+ LOG(PluginEvents, "NPP_HandleEvent(getFocusEvent): %d", acceptedEvent);
+ installKeyEventHandler();
+ } else {
+ event.what = loseFocusEvent;
+ acceptedEvent = sendEvent(&event);
+ LOG(PluginEvents, "NPP_HandleEvent(loseFocusEvent): %d", acceptedEvent);
+ removeKeyEventHandler();
+ }
+}
+
+void WebNetscapePluginEventHandlerCarbon::windowFocusChanged(bool hasFocus)
+{
+ WindowRef windowRef = (WindowRef)[[m_pluginView window] windowRef];
+
+ SetUserFocusWindow(windowRef);
+
+ EventRecord event;
+
+ getCarbonEvent(&event);
+ event.what = activateEvt;
+ event.message = (unsigned long)windowRef;
+ if (hasFocus)
+ event.modifiers |= activeFlag;
+
+ BOOL acceptedEvent;
+ acceptedEvent = sendEvent(&event);
+
+ LOG(PluginEvents, "NPP_HandleEvent(activateEvent): %d isActive: %d", acceptedEvent, hasFocus);
+}
+
+OSStatus WebNetscapePluginEventHandlerCarbon::TSMEventHandler(EventHandlerCallRef inHandlerRef, EventRef inEvent, void *eventHandler)
+{
+ EventRef rawKeyEventRef;
+ OSStatus status = GetEventParameter(inEvent, kEventParamTextInputSendKeyboardEvent, typeEventRef, NULL, sizeof(EventRef), NULL, &rawKeyEventRef);
+ if (status != noErr) {
+ LOG_ERROR("GetEventParameter failed with error: %d", status);
+ return noErr;
+ }
+
+ // Two-pass read to allocate/extract Mac charCodes
+ ByteCount numBytes;
+ status = GetEventParameter(rawKeyEventRef, kEventParamKeyMacCharCodes, typeChar, NULL, 0, &numBytes, NULL);
+ if (status != noErr) {
+ LOG_ERROR("GetEventParameter failed with error: %d", status);
+ return noErr;
+ }
+ char *buffer = (char *)malloc(numBytes);
+ status = GetEventParameter(rawKeyEventRef, kEventParamKeyMacCharCodes, typeChar, NULL, numBytes, NULL, buffer);
+ if (status != noErr) {
+ LOG_ERROR("GetEventParameter failed with error: %d", status);
+ free(buffer);
+ return noErr;
+ }
+
+ EventRef cloneEvent = CopyEvent(rawKeyEventRef);
+ unsigned i;
+ for (i = 0; i < numBytes; i++) {
+ status = SetEventParameter(cloneEvent, kEventParamKeyMacCharCodes, typeChar, 1 /* one char code */, &buffer[i]);
+ if (status != noErr) {
+ LOG_ERROR("SetEventParameter failed with error: %d", status);
+ free(buffer);
+ return noErr;
+ }
+
+ EventRecord eventRec;
+ if (ConvertEventRefToEventRecord(cloneEvent, &eventRec)) {
+ BOOL acceptedEvent;
+ acceptedEvent = static_cast<WebNetscapePluginEventHandlerCarbon*>(eventHandler)->sendEvent(&eventRec);
+
+ LOG(PluginEvents, "NPP_HandleEvent(keyDown): %d charCode:%c keyCode:%lu",
+ acceptedEvent, (char) (eventRec.message & charCodeMask), (eventRec.message & keyCodeMask));
+
+ // We originally thought that if the plug-in didn't accept this event,
+ // we should pass it along so that keyboard scrolling, for example, will work.
+ // In practice, this is not a good idea, because plug-ins tend to eat the event but return false.
+ // MacIE handles each key event twice because of this, but we will emulate the other browsers instead.
+ }
+ }
+ ReleaseEvent(cloneEvent);
+
+ free(buffer);
+
+ return noErr;
+}
+
+void WebNetscapePluginEventHandlerCarbon::installKeyEventHandler()
+{
+ static const EventTypeSpec sTSMEvents[] =
+ {
+ { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent }
+ };
+
+ if (!m_keyEventHandler) {
+ InstallEventHandler(GetWindowEventTarget((WindowRef)[[m_pluginView window] windowRef]),
+ NewEventHandlerUPP(TSMEventHandler),
+ GetEventTypeCount(sTSMEvents),
+ sTSMEvents,
+ this,
+ &m_keyEventHandler);
+ }
+}
+
+void WebNetscapePluginEventHandlerCarbon::removeKeyEventHandler()
+{
+ if (m_keyEventHandler) {
+ RemoveEventHandler(m_keyEventHandler);
+ m_keyEventHandler = 0;
+ }
+}
+
+void WebNetscapePluginEventHandlerCarbon::nullEventTimerFired(CFRunLoopTimerRef timerRef, void *context)
+{
+ static_cast<WebNetscapePluginEventHandlerCarbon*>(context)->sendNullEvent();
+}
+
+void WebNetscapePluginEventHandlerCarbon::startTimers(bool throttleTimers)
+{
+ ASSERT(!m_nullEventTimer);
+
+ CFTimeInterval interval = !throttleTimers ? NullEventIntervalActive : NullEventIntervalNotActive;
+
+ CFRunLoopTimerContext context = { 0, this, NULL, NULL, NULL };
+ m_nullEventTimer.adoptCF(CFRunLoopTimerCreate(0, CFAbsoluteTimeGetCurrent() + interval, interval,
+ 0, 0, nullEventTimerFired, &context));
+ CFRunLoopAddTimer(CFRunLoopGetCurrent(), m_nullEventTimer.get(), kCFRunLoopDefaultMode);
+}
+
+void WebNetscapePluginEventHandlerCarbon::stopTimers()
+{
+ if (!m_nullEventTimer)
+ return;
+
+ CFRunLoopTimerInvalidate(m_nullEventTimer.get());
+ m_nullEventTimer = 0;
+}
+
+void* WebNetscapePluginEventHandlerCarbon::platformWindow(NSWindow* window)
+{
+ return [window windowRef];
+}
+
+bool WebNetscapePluginEventHandlerCarbon::sendEvent(EventRecord* event)
+{
+ // If at any point the user clicks or presses a key from within a plugin, set the
+ // currentEventIsUserGesture flag to true. This is important to differentiate legitimate
+ // window.open() calls; we still want to allow those. See rdar://problem/4010765
+ if (event->what == ::mouseDown || event->what == ::keyDown || event->what == ::mouseUp || event->what == ::autoKey)
+ m_currentEventIsUserGesture = true;
+
+ m_suspendKeyUpEvents = false;
+
+ bool result = [m_pluginView sendEvent:event isDrawRect:event->what == updateEvt];
+
+ m_currentEventIsUserGesture = false;
+
+ return result;
+}
+
+#endif // ENABLE(NETSCAPE_PLUGIN_API) && !defined(__LP64__)