diff -r 000000000000 -r 4f2f89ce4247 WebKit/mac/Plugins/WebNetscapePluginEventHandlerCarbon.mm --- /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(carbonEvent->where.h * HIGetScaleFactor()); + carbonEvent->where.v = static_cast(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(carbonEvent->where.h * HIGetScaleFactor()); + carbonEvent->where.v = static_cast(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(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(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__)