| /* |
| * Copyright (C) 2008-2017 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 <HIToolbox/CarbonEvents.h> |
| #import <pal/spi/mac/HIToolboxSPI.h> |
| #import <pal/spi/mac/NSEventSPI.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->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 != NSEventTypeLeftMouseDown && eventType != NSEventTypeRightMouseDown) |
| modifiers |= btnState; |
| |
| if (modifierFlags & NSEventModifierFlagCommand) |
| modifiers |= cmdKey; |
| |
| if (modifierFlags & NSEventModifierFlagShift) |
| modifiers |= shiftKey; |
| |
| if (modifierFlags & NSEventModifierFlagCapsLock) |
| modifiers |= alphaLock; |
| |
| if (modifierFlags & NSEventModifierFlagOption) |
| modifiers |= optionKey; |
| |
| if (modifierFlags & NSEventModifierFlagControl || eventType == NSEventTypeRightMouseDown) |
| modifiers |= controlKey; |
| |
| return modifiers; |
| } |
| |
| static void getCarbonEvent(EventRecord *carbonEvent, NSEvent *cocoaEvent) |
| { |
| if ([cocoaEvent _eventRef] && ConvertEventRefToEventRecord((EventRef)[cocoaEvent _eventRef], carbonEvent)) |
| 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 *)[[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 = NPEventType_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 = NPEventType_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 = NPEventType_AdjustCursorEvent; |
| |
| BOOL acceptedEvent; |
| acceptedEvent = sendEvent(&event); |
| |
| LOG(PluginEvents, "NPP_HandleEvent(mouseMoved): %d", acceptedEvent); |
| } |
| |
| void WebNetscapePluginEventHandlerCarbon::keyDown(NSEvent *theEvent) |
| { |
| m_suspendKeyUpEvents = true; |
| TSMProcessRawKeyEvent((EventRef)[theEvent _eventRef]); |
| } |
| |
| 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) |
| { |
| TSMProcessRawKeyEvent((EventRef)[theEvent _eventRef]); |
| |
| // 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 = NPEventType_GetFocusEvent; |
| acceptedEvent = sendEvent(&event); |
| LOG(PluginEvents, "NPP_HandleEvent(NPEventType_GetFocusEvent): %d", acceptedEvent); |
| installKeyEventHandler(); |
| } else { |
| event.what = NPEventType_LoseFocusEvent; |
| acceptedEvent = sendEvent(&event); |
| LOG(PluginEvents, "NPP_HandleEvent(NPEventType_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__) |