| /* |
| * Copyright (C) 2005-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. |
| * 3. Neither the name of Apple Inc. ("Apple") nor the names of |
| * its contributors may be used to endorse or promote products derived |
| * from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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. |
| */ |
| |
| #ifndef __LP64__ |
| |
| #import "HIWebView.h" |
| |
| #import "CarbonWindowAdapter.h" |
| #import "HIViewAdapter.h" |
| #import "WebHTMLViewInternal.h" |
| #import "WebKit.h" |
| #import <pal/spi/mac/NSEventSPI.h> |
| #import <pal/spi/mac/NSGraphicsSPI.h> |
| #import <pal/spi/mac/QuickDrawSPI.h> |
| #import <wtf/Assertions.h> |
| #import <wtf/ObjcRuntimeExtras.h> |
| #import <wtf/cf/TypeCastsCF.h> |
| |
| WTF_DECLARE_CF_TYPE_TRAIT(CFRunLoop); |
| |
| @interface NSWindow (AppKitSecretsHIWebViewKnows) |
| - (void)_removeWindowRef; |
| @end |
| |
| @interface NSView (AppKitSecretsHIWebViewKnows) |
| - (void)_clearDirtyRectsForTree; |
| @end |
| |
| extern "C" void HIWebViewRegisterClass(); |
| |
| @interface MenuItemProxy : NSObject <NSValidatedUserInterfaceItem> |
| { |
| int _tag; |
| SEL _action; |
| } |
| |
| - (id)initWithAction:(SEL)action; |
| - (SEL)action; |
| - (int)tag; |
| |
| @end |
| |
| @implementation MenuItemProxy |
| |
| - (id)initWithAction:(SEL)action |
| { |
| [super init]; |
| if (self == nil) return nil; |
| |
| _action = action; |
| |
| return self; |
| } |
| |
| - (SEL)action |
| { |
| return _action; |
| } |
| |
| - (int)tag |
| { |
| return 0; |
| } |
| |
| @end |
| |
| @interface NSWindowGraphicsContext (HIWebView) |
| - (void)_web_setGraphicsPort:(CGContextRef)context; |
| @end |
| |
| @implementation NSWindowGraphicsContext (HIWebView) |
| |
| - (void)_web_setGraphicsPort:(CGContextRef)context |
| { |
| CGContextRetain(context); |
| CGContextRelease(_cgsContext); |
| _cgsContext = context; |
| } |
| |
| @end |
| |
| struct HIWebView |
| { |
| HIViewRef fViewRef; |
| |
| WebView* fWebView; |
| NSView* fFirstResponder; |
| CarbonWindowAdapter* fKitWindow; |
| bool fIsComposited; |
| CFRunLoopObserverRef fUpdateObserver; |
| }; |
| typedef struct HIWebView HIWebView; |
| |
| static const OSType NSAppKitPropertyCreator = 'akit'; |
| /* |
| These constants are not used. Commented out to make the compiler happy. |
| static const OSType NSViewCarbonControlViewPropertyTag = 'view'; |
| static const OSType NSViewCarbonControlAutodisplayPropertyTag = 'autd'; |
| static const OSType NSViewCarbonControlFirstResponderViewPropertyTag = 'frvw'; |
| */ |
| static const OSType NSCarbonWindowPropertyTag = 'win '; |
| |
| |
| static SEL _NSSelectorForHICommand(const HICommand*); |
| |
| static const EventTypeSpec kEvents[] = { |
| { kEventClassHIObject, kEventHIObjectConstruct }, |
| { kEventClassHIObject, kEventHIObjectDestruct }, |
| |
| { kEventClassMouse, kEventMouseUp }, |
| { kEventClassMouse, kEventMouseMoved }, |
| { kEventClassMouse, kEventMouseDragged }, |
| { kEventClassMouse, kEventMouseWheelMoved }, |
| |
| { kEventClassKeyboard, kEventRawKeyDown }, |
| { kEventClassKeyboard, kEventRawKeyRepeat }, |
| |
| { kEventClassCommand, kEventCommandProcess }, |
| { kEventClassCommand, kEventCommandUpdateStatus }, |
| |
| { kEventClassControl, kEventControlInitialize }, |
| { kEventClassControl, kEventControlDraw }, |
| { kEventClassControl, kEventControlHitTest }, |
| { kEventClassControl, kEventControlGetPartRegion }, |
| { kEventClassControl, kEventControlGetData }, |
| { kEventClassControl, kEventControlBoundsChanged }, |
| { kEventClassControl, kEventControlActivate }, |
| { kEventClassControl, kEventControlDeactivate }, |
| { kEventClassControl, kEventControlOwningWindowChanged }, |
| { kEventClassControl, kEventControlClick }, |
| { kEventClassControl, kEventControlContextualMenuClick }, |
| { kEventClassControl, kEventControlSetFocusPart } |
| }; |
| |
| #define kHIViewBaseClassID CFSTR("com.apple.hiview") |
| #define kHIWebViewClassID CFSTR("com.apple.HIWebView") |
| |
| static HIWebView* HIWebViewConstructor(HIViewRef inView); |
| static void HIWebViewDestructor(HIWebView*); |
| |
| static OSStatus HIWebViewEventHandler(EventHandlerCallRef inCallRef, EventRef inEvent, void *inUserData); |
| |
| static UInt32 GetBehaviors(); |
| static ControlKind GetKind(); |
| static void Draw(HIWebView* inView, RgnHandle limitRgn, CGContextRef inContext); |
| static ControlPartCode HitTest(HIWebView*, const HIPoint* where); |
| static OSStatus GetRegion(HIWebView*, ControlPartCode inPart, RgnHandle outRgn); |
| static void BoundsChanged(HIWebView* inView, UInt32 inOptions, const HIRect* inOriginalBounds, const HIRect* inCurrentBounds); |
| static void OwningWindowChanged(HIWebView*, WindowRef oldWindow, WindowRef newWindow); |
| static void ActiveStateChanged(HIWebView*); |
| |
| static OSStatus Click(HIWebView* inView, EventRef inEvent); |
| static OSStatus ContextMenuClick(HIWebView* inView, EventRef inEvent); |
| static OSStatus MouseUp(HIWebView* inView, EventRef inEvent); |
| static OSStatus MouseMoved(HIWebView* inView, EventRef inEvent); |
| static OSStatus MouseDragged(HIWebView* inView, EventRef inEvent); |
| static OSStatus MouseWheelMoved(HIWebView* inView, EventRef inEvent); |
| |
| static OSStatus ProcessCommand(HIWebView* inView, const HICommand* inCommand); |
| static OSStatus UpdateCommandStatus(HIWebView* inView, const HICommand* inCommand); |
| |
| static OSStatus SetFocusPart(HIWebView*, ControlPartCode desiredFocus, RgnHandle invalidRgn, Boolean focusEverything, ControlPartCode* actualFocus); |
| static NSView* AdvanceFocus(HIWebView*, bool forward); |
| static void RelinquishFocus(HIWebView*, bool inAutodisplay); |
| |
| static WindowRef GetWindowRef(HIWebView* inView); |
| static void SyncFrame(HIWebView* inView); |
| |
| static OSStatus WindowHandler(EventHandlerCallRef inCallRef, EventRef inEvent, void* inUserData); |
| |
| static void StartUpdateObserver(HIWebView*); |
| static void StopUpdateObserver(HIWebView*); |
| |
| //---------------------------------------------------------------------------------- |
| // HIWebViewCreate |
| //---------------------------------------------------------------------------------- |
| // |
| OSStatus HIWebViewCreate(HIViewRef* outControl) |
| { |
| HIWebViewRegisterClass(); |
| return HIObjectCreate(kHIWebViewClassID, nullptr, (HIObjectRef*)outControl); |
| } |
| |
| //---------------------------------------------------------------------------------- |
| // HIWebViewGetWebView |
| //---------------------------------------------------------------------------------- |
| // |
| WebView* HIWebViewGetWebView(HIViewRef inView) |
| { |
| HIWebView* view = (HIWebView*)HIObjectDynamicCast((HIObjectRef)inView, kHIWebViewClassID); |
| WebView* result = nullptr; |
| |
| if (view) |
| result = view->fWebView; |
| |
| return result; |
| } |
| |
| //---------------------------------------------------------------------------------- |
| // HIWebViewConstructor |
| //---------------------------------------------------------------------------------- |
| // |
| |
| static HIWebView* HIWebViewConstructor(HIViewRef inView) |
| { |
| HIWebView* view = (HIWebView*)malloc(sizeof(HIWebView)); |
| |
| if (view) { |
| NSRect frame = { { 0, 0 }, { 400, 400 } }; |
| |
| view->fViewRef = inView; |
| |
| WebView *webView = [[WebView alloc] initWithFrame: frame]; |
| CFRetain(webView); |
| [webView release]; |
| view->fWebView = webView; |
| [HIViewAdapter bindHIViewToNSView:inView nsView:view->fWebView]; |
| |
| view->fFirstResponder = nullptr; |
| view->fKitWindow = nullptr; |
| view->fIsComposited = false; |
| view->fUpdateObserver = nullptr; |
| } |
| |
| return view; |
| } |
| |
| //---------------------------------------------------------------------------------- |
| // HIWebViewDestructor |
| //---------------------------------------------------------------------------------- |
| // |
| static void HIWebViewDestructor(HIWebView* inView) |
| { |
| [HIViewAdapter unbindNSView:inView->fWebView]; |
| CFRelease(inView->fWebView); |
| |
| free(inView); |
| } |
| |
| //---------------------------------------------------------------------------------- |
| // HIWebViewRegisterClass |
| //---------------------------------------------------------------------------------- |
| // |
| void HIWebViewRegisterClass() |
| { |
| static bool sRegistered; |
| |
| if (!sRegistered) { |
| HIObjectRegisterSubclass(kHIWebViewClassID, kHIViewBaseClassID, 0, HIWebViewEventHandler, |
| GetEventTypeCount(kEvents), kEvents, 0, nullptr); |
| sRegistered = true; |
| } |
| } |
| |
| //---------------------------------------------------------------------------------- |
| // GetBehaviors |
| //---------------------------------------------------------------------------------- |
| // |
| static UInt32 GetBehaviors() |
| { |
| return kControlSupportsDataAccess | kControlSupportsGetRegion | kControlGetsFocusOnClick; |
| } |
| |
| static CGContextRef overrideCGContext(NSWindow *window, CGContextRef context) |
| { |
| #pragma clang diagnostic push |
| #pragma clang diagnostic ignored "-Wdeprecated-declarations" |
| NSWindowGraphicsContext *graphicsContext = (NSWindowGraphicsContext *)window.graphicsContext; |
| CGContextRef savedContext = (CGContextRef)graphicsContext.graphicsPort; |
| #pragma clang diagnostic pop |
| CGContextRetain(savedContext); |
| [graphicsContext _web_setGraphicsPort:context]; |
| return savedContext; |
| } |
| |
| static void restoreCGContext(NSWindow *window, CGContextRef savedContext) |
| { |
| #pragma clang diagnostic push |
| #pragma clang diagnostic ignored "-Wdeprecated-declarations" |
| NSWindowGraphicsContext *graphicsContext = (NSWindowGraphicsContext *)window.graphicsContext; |
| #pragma clang diagnostic pop |
| [graphicsContext _web_setGraphicsPort:savedContext]; |
| CGContextRelease(savedContext); |
| } |
| |
| //---------------------------------------------------------------------------------- |
| // Draw |
| //---------------------------------------------------------------------------------- |
| // |
| static void Draw(HIWebView* inView, RgnHandle limitRgn, CGContextRef inContext) |
| { |
| HIRect bounds; |
| Rect drawRect; |
| HIRect hiRect; |
| bool createdContext = false; |
| |
| if (!inView->fIsComposited) { |
| GrafPtr port; |
| Rect portRect; |
| |
| #pragma clang diagnostic push |
| #pragma clang diagnostic ignored "-Wdeprecated-declarations" |
| GetPort(&port); |
| GetPortBounds(port, &portRect); |
| CreateCGContextForPort(port, &inContext); |
| #pragma clang diagnostic push |
| SyncCGContextOriginWithPort(inContext, port); |
| CGContextTranslateCTM(inContext, 0, (portRect.bottom - portRect.top)); |
| CGContextScaleCTM(inContext, 1, -1); |
| createdContext = true; |
| } |
| |
| HIViewGetBounds(inView->fViewRef, &bounds); |
| |
| CGContextRef savedContext = overrideCGContext(inView->fKitWindow, inContext); |
| [NSGraphicsContext setCurrentContext:[inView->fKitWindow graphicsContext]]; |
| |
| GetRegionBounds(limitRgn, &drawRect); |
| |
| if (!inView->fIsComposited) |
| OffsetRect(&drawRect, (SInt16)-bounds.origin.x, (SInt16)-bounds.origin.y); |
| |
| hiRect.origin.x = drawRect.left; |
| hiRect.origin.y = bounds.size.height - drawRect.bottom; // flip y |
| hiRect.size.width = drawRect.right - drawRect.left; |
| hiRect.size.height = drawRect.bottom - drawRect.top; |
| |
| // printf("Drawing: drawRect is (%g %g) (%g %g)\n", hiRect.origin.x, hiRect.origin.y, |
| // hiRect.size.width, hiRect.size.height); |
| |
| // FIXME: We need to do layout before Carbon has decided what region needs drawn. |
| // In Cocoa we make sure to do layout and invalidate any new regions before draw, so everything |
| // can be drawn in one pass. Doing a layout here will cause new regions to be invalidated, but they |
| // will not all be drawn in this pass since we already have a fixed rect we are going to display. |
| |
| NSView <WebDocumentView> *documentView = [[[inView->fWebView mainFrame] frameView] documentView]; |
| if ([documentView isKindOfClass:[WebHTMLView class]]) |
| [(WebHTMLView *)documentView _web_updateLayoutAndStyleIfNeededRecursive]; |
| |
| if (inView->fIsComposited) |
| [inView->fWebView displayIfNeededInRect: *(NSRect*)&hiRect]; |
| else |
| [inView->fWebView displayRect:*(NSRect*)&hiRect]; |
| |
| restoreCGContext(inView->fKitWindow, savedContext); |
| |
| if (!inView->fIsComposited) |
| { |
| HIViewRef view; |
| HIViewFindByID(HIViewGetRoot(GetControlOwner(inView->fViewRef)), kHIViewWindowGrowBoxID, &view); |
| if (view) |
| { |
| HIRect frame; |
| |
| HIViewGetBounds(view, &frame); |
| HIViewConvertRect(&frame, view, nullptr); |
| |
| hiRect.origin.x = drawRect.left; |
| hiRect.origin.y = drawRect.top; |
| hiRect.size.width = drawRect.right - drawRect.left; |
| hiRect.size.height = drawRect.bottom - drawRect.top; |
| |
| HIViewConvertRect(&hiRect, inView->fViewRef, nullptr); |
| |
| if (CGRectIntersectsRect(frame, hiRect)) |
| HIViewSetNeedsDisplay(view, true); |
| } |
| } |
| |
| if (createdContext) |
| { |
| CGContextSynchronize(inContext); |
| CGContextRelease(inContext); |
| } |
| } |
| |
| //---------------------------------------------------------------------------------- |
| // HitTest |
| //---------------------------------------------------------------------------------- |
| // |
| static ControlPartCode HitTest(HIWebView* view, const HIPoint* where) |
| { |
| HIRect bounds; |
| |
| HIViewGetBounds(view->fViewRef, &bounds); |
| |
| if (CGRectContainsPoint(bounds, *where)) |
| return 1; |
| return kControlNoPart; |
| } |
| |
| //---------------------------------------------------------------------------------- |
| // GetRegion |
| //---------------------------------------------------------------------------------- |
| // |
| static OSStatus GetRegion(HIWebView* inView, ControlPartCode inPart, RgnHandle outRgn) |
| { |
| OSStatus err = eventNotHandledErr; |
| |
| if (inPart == -3 && [inView->fWebView isOpaque]) { |
| // kControlOpaqueMetaPart: |
| HIRect bounds; |
| Rect temp; |
| |
| HIViewGetBounds(inView->fViewRef, &bounds); |
| |
| temp.top = (SInt16)bounds.origin.y; |
| temp.left = (SInt16)bounds.origin.x; |
| temp.bottom = (SInt16)CGRectGetMaxY(bounds); |
| temp.right = (SInt16)CGRectGetMaxX(bounds); |
| |
| RectRgn(outRgn, &temp); |
| err = noErr; |
| } |
| |
| return err; |
| } |
| |
| static WindowRef GetWindowRef(HIWebView* inView) |
| { |
| return GetControlOwner(inView->fViewRef); |
| } |
| |
| static NSEvent *CreateNSEventAdoptingCGEvent(CGEventRef cgEvent, EventRef eventRef) |
| { |
| NSEvent *result = [[NSEvent alloc] _initWithCGEvent:cgEvent eventRef:eventRef]; |
| CFRelease(cgEvent); |
| return result; |
| } |
| |
| static Boolean |
| CopyEventCGEvent(EventRef event, CGEventRef* cgEvent) |
| { |
| if ((*cgEvent = CopyEventCGEvent(event))) |
| return true; |
| |
| // This event might not have been created directly from a CGS event, and might not |
| // have a CGEventRef associated with it. In that case, try using the event most |
| // recently dispatched by the event dispatcher, which is likely to be the original |
| // user-input event containing a CGEventRef. |
| event = GetCurrentEvent(); |
| if (event && (*cgEvent = CopyEventCGEvent(event))) |
| return true; |
| return false; |
| } |
| |
| static NSEvent *CreateNSEventWithCarbonClickEvent(EventRef inEvent, WindowRef windowRef) |
| { |
| EventRef newEvent; |
| Point where; |
| OSStatus err; |
| UInt32 modifiers; |
| Rect windRect; |
| |
| CGEventRef cgEvent = nullptr; |
| if (!CopyEventCGEvent(inEvent, &cgEvent)) |
| return nil; |
| |
| // We need to make the event be a kEventMouseDown event, or the webkit might trip up when |
| // we click on a Netscape plugin. It calls ConvertEventRefToEventRecord, assuming |
| // that mouseDown was passed an event with a real mouse down eventRef. We just need a |
| // minimal one here. |
| |
| err = CreateEvent(nullptr, kEventClassMouse, kEventMouseDown, GetEventTime(inEvent), 0, &newEvent); |
| if (err != noErr) |
| return nil; |
| |
| GetEventParameter(inEvent, kEventParamWindowMouseLocation, typeQDPoint, nullptr, sizeof(Point), nullptr, &where); |
| GetWindowBounds(windowRef, kWindowStructureRgn, &windRect); |
| where.h += windRect.left; |
| where.v += windRect.top; |
| |
| GetEventParameter(inEvent, kEventParamKeyModifiers, typeUInt32, nullptr, sizeof(UInt32), nullptr, &modifiers); |
| SetEventParameter(newEvent, kEventParamMouseLocation, typeQDPoint, sizeof(Point), &where); |
| SetEventParameter(newEvent, kEventParamKeyModifiers, typeUInt32, sizeof(UInt32), &modifiers); |
| |
| return CreateNSEventAdoptingCGEvent(cgEvent, newEvent); |
| } |
| |
| //---------------------------------------------------------------------------------- |
| // Click |
| //---------------------------------------------------------------------------------- |
| // |
| static OSStatus Click(HIWebView* inView, EventRef inEvent) |
| { |
| NSEvent *kitEvent = CreateNSEventWithCarbonClickEvent(inEvent, GetWindowRef(inView)); |
| |
| if (!inView->fIsComposited) |
| StartUpdateObserver(inView); |
| |
| [inView->fKitWindow sendEvent:kitEvent]; |
| |
| if (!inView->fIsComposited) |
| StopUpdateObserver(inView); |
| |
| [kitEvent release]; |
| |
| return noErr; |
| } |
| |
| static NSEvent *CreateNSEventWithCarbonEvent(EventRef inEvent) |
| { |
| NSEvent *event = [NSEvent eventWithEventRef:inEvent]; |
| if (!event) |
| event = [NSEvent eventWithEventRef:GetCurrentEvent()]; |
| |
| return [event retain]; |
| } |
| |
| //---------------------------------------------------------------------------------- |
| // MouseUp |
| //---------------------------------------------------------------------------------- |
| // |
| static OSStatus MouseUp(HIWebView* inView, EventRef inEvent) |
| { |
| NSEvent* kitEvent = CreateNSEventWithCarbonEvent(inEvent); |
| |
| [inView->fKitWindow sendEvent:kitEvent]; |
| |
| [kitEvent release]; |
| |
| return noErr; |
| } |
| |
| static NSEvent *CreateNSEventWithCarbonMouseMoveEvent(EventRef inEvent, NSWindow *window) |
| { |
| NSEvent* kitEvent = [NSEvent eventWithEventRef:inEvent]; |
| |
| // FIXME: Works around bug 3585644. Can remove it once that bug is fixed. |
| // We preflight here and don't do any work when the window is already correct |
| // because _eventRelativeToWindow will malfunction if the event's window method |
| // has been hijacked by the bug workaround used by Contribute. It's fine to just |
| // leave the event alone if the window is already correct. |
| |
| if ([kitEvent window] != window) |
| kitEvent = [kitEvent _eventRelativeToWindow:window]; |
| |
| return [kitEvent retain]; |
| } |
| |
| //---------------------------------------------------------------------------------- |
| // MouseMoved |
| //---------------------------------------------------------------------------------- |
| // |
| static OSStatus MouseMoved(HIWebView* inView, EventRef inEvent) |
| { |
| NSEvent *kitEvent = CreateNSEventWithCarbonMouseMoveEvent(inEvent, inView->fKitWindow); |
| [inView->fKitWindow sendEvent:kitEvent]; |
| [kitEvent release]; |
| |
| return noErr; |
| } |
| |
| //---------------------------------------------------------------------------------- |
| // MouseDragged |
| //---------------------------------------------------------------------------------- |
| // |
| static OSStatus MouseDragged(HIWebView* inView, EventRef inEvent) |
| { |
| NSEvent* kitEvent = CreateNSEventWithCarbonEvent(inEvent); |
| |
| [inView->fKitWindow sendEvent:kitEvent]; |
| |
| [kitEvent release]; |
| |
| return noErr; |
| } |
| |
| //---------------------------------------------------------------------------------- |
| // MouseDragged |
| //---------------------------------------------------------------------------------- |
| // |
| static OSStatus MouseWheelMoved(HIWebView* inView, EventRef inEvent) |
| { |
| NSEvent* kitEvent = CreateNSEventWithCarbonEvent(inEvent); |
| |
| [inView->fKitWindow sendEvent:kitEvent]; |
| |
| [kitEvent release]; |
| |
| return noErr; |
| } |
| |
| //---------------------------------------------------------------------------------- |
| // ContextMenuClick |
| //---------------------------------------------------------------------------------- |
| // |
| static OSStatus ContextMenuClick(HIWebView* inView, EventRef inEvent) |
| { |
| NSView *webView = inView->fWebView; |
| NSWindow *window = [webView window]; |
| |
| // Get the point out of the event. |
| HIPoint point; |
| GetEventParameter(inEvent, kEventParamMouseLocation, typeHIPoint, nullptr, sizeof(point), nullptr, &point); |
| HIViewConvertPoint(&point, inView->fViewRef, nullptr); |
| |
| // Flip the Y coordinate, since Carbon is flipped relative to the AppKit. |
| NSPoint location = NSMakePoint(point.x, [window frame].size.height - point.y); |
| |
| // Make up an event with the point and send it to the window. |
| NSEvent *kitEvent = [NSEvent mouseEventWithType:NSEventTypeRightMouseDown |
| location:location |
| modifierFlags:0 |
| timestamp:GetEventTime(inEvent) |
| windowNumber:[window windowNumber] |
| context:0 |
| eventNumber:0 |
| clickCount:1 |
| pressure:0]; |
| [inView->fKitWindow sendEvent:kitEvent]; |
| return noErr; |
| } |
| |
| //---------------------------------------------------------------------------------- |
| // GetKind |
| //---------------------------------------------------------------------------------- |
| // |
| static ControlKind GetKind() |
| { |
| const ControlKind kMyKind = { 'appl', 'wbvw' }; |
| |
| return kMyKind; |
| } |
| |
| //---------------------------------------------------------------------------------- |
| // BoundsChanged |
| //---------------------------------------------------------------------------------- |
| // |
| static void BoundsChanged(HIWebView* inView, UInt32 inOptions, const HIRect* inOriginalBounds, const HIRect* inCurrentBounds) |
| { |
| if (inView->fWebView) |
| SyncFrame(inView); |
| } |
| |
| //---------------------------------------------------------------------------------- |
| // OwningWindowChanged |
| //---------------------------------------------------------------------------------- |
| // |
| static void |
| OwningWindowChanged(HIWebView* view, WindowRef oldWindow, WindowRef newWindow) |
| { |
| if (newWindow) { |
| WindowAttributes attrs; |
| |
| OSStatus err = GetWindowProperty(newWindow, NSAppKitPropertyCreator, NSCarbonWindowPropertyTag, sizeof(NSWindow *), nullptr, &view->fKitWindow); |
| if (err != noErr) { |
| const EventTypeSpec kWindowEvents[] = { |
| { kEventClassWindow, kEventWindowClosed }, |
| { kEventClassMouse, kEventMouseMoved }, |
| { kEventClassMouse, kEventMouseUp }, |
| { kEventClassMouse, kEventMouseDragged }, |
| { kEventClassMouse, kEventMouseWheelMoved }, |
| { kEventClassKeyboard, kEventRawKeyDown }, |
| { kEventClassKeyboard, kEventRawKeyRepeat }, |
| { kEventClassKeyboard, kEventRawKeyUp }, |
| { kEventClassControl, kEventControlClick }, |
| }; |
| |
| view->fKitWindow = [[CarbonWindowAdapter alloc] initWithCarbonWindowRef: newWindow takingOwnership: NO disableOrdering:NO carbon:YES]; |
| SetWindowProperty(newWindow, NSAppKitPropertyCreator, NSCarbonWindowPropertyTag, sizeof(NSWindow *), &view->fKitWindow); |
| |
| InstallWindowEventHandler(newWindow, WindowHandler, GetEventTypeCount(kWindowEvents), kWindowEvents, newWindow, nullptr); |
| } |
| |
| [[view->fKitWindow contentView] addSubview:view->fWebView]; |
| |
| GetWindowAttributes(newWindow, &attrs); |
| view->fIsComposited = attrs & kWindowCompositingAttribute; |
| |
| SyncFrame(view); |
| } else { |
| // Be sure to detach the cocoa view, too. |
| if (view->fWebView) |
| [view->fWebView removeFromSuperview]; |
| |
| view->fKitWindow = nullptr; // break the ties that bind |
| } |
| } |
| |
| //------------------------------------------------------------------------------------- |
| // WindowHandler |
| //------------------------------------------------------------------------------------- |
| // Redirect mouse events to the views beneath them. This is required for WebKit to work |
| // properly. We install it once per window. We also tap into window close to release |
| // the NSWindow that shadows our Carbon window. |
| // |
| static OSStatus WindowHandler(EventHandlerCallRef inCallRef, EventRef inEvent, void* inUserData) |
| { |
| WindowRef window = (WindowRef)inUserData; |
| OSStatus result = eventNotHandledErr; |
| |
| NSWindow* kitWindow; |
| OSStatus err = noErr; |
| NSEvent* kitEvent; |
| |
| switch (GetEventClass(inEvent)) { |
| case kEventClassControl: |
| switch (GetEventKind(inEvent)) { |
| case kEventControlClick: |
| CarbonWindowAdapter *kitWindow; |
| |
| err = GetWindowProperty(window, NSAppKitPropertyCreator, NSCarbonWindowPropertyTag, sizeof(NSWindow *), nullptr, &kitWindow); |
| |
| // We must be outside the HIWebView, relinquish focus. |
| [kitWindow relinquishFocus]; |
| break; |
| } |
| break; |
| |
| case kEventClassKeyboard: |
| // if the first responder in the kit window is something other than the |
| // window, we assume a subview of the webview is focused. we must send |
| // the event to the window so that it goes through the kit's normal TSM |
| // logic, and -- more importantly -- allows any delegates associated |
| // with the first responder to have a chance at the event. |
| |
| err = GetWindowProperty(window, NSAppKitPropertyCreator, NSCarbonWindowPropertyTag, sizeof(NSWindow *), nullptr, &kitWindow); |
| if (err == noErr) { |
| NSResponder* responder = [kitWindow firstResponder]; |
| if (responder != kitWindow) { |
| kitEvent = CreateNSEventWithCarbonEvent(inEvent); |
| |
| [kitWindow sendEvent:kitEvent]; |
| [kitEvent release]; |
| |
| result = noErr; |
| } |
| } |
| break; |
| |
| case kEventClassWindow: |
| |
| |
| err = GetWindowProperty(window, NSAppKitPropertyCreator, NSCarbonWindowPropertyTag, sizeof(NSWindow *), nullptr, &kitWindow); |
| if (err == noErr) { |
| [kitWindow _removeWindowRef]; |
| [kitWindow close]; |
| } |
| result = noErr; |
| break; |
| |
| case kEventClassMouse: |
| switch (GetEventKind(inEvent)) { |
| case kEventMouseMoved: |
| Point where; |
| GetEventParameter(inEvent, kEventParamMouseLocation, typeQDPoint, nullptr, sizeof(Point), nullptr, &where); |
| |
| WindowRef temp; |
| FindWindow(where, &temp); |
| if (temp == window) { |
| Rect bounds; |
| GetWindowBounds(window, kWindowStructureRgn, &bounds); |
| where.h -= bounds.left; |
| where.v -= bounds.top; |
| SetEventParameter(inEvent, kEventParamWindowRef, typeWindowRef, sizeof(WindowRef), &window); |
| SetEventParameter(inEvent, kEventParamWindowMouseLocation, typeQDPoint, sizeof(Point), &where); |
| |
| HIViewRef view = nullptr; |
| |
| err = HIViewGetViewForMouseEvent(HIViewGetRoot(window), inEvent, &view); |
| if (err == noErr && view && HIObjectIsOfClass((HIObjectRef)view, kHIWebViewClassID)) |
| result = SendEventToEventTargetWithOptions(inEvent, HIObjectGetEventTarget((HIObjectRef)view), kEventTargetDontPropagate); |
| } |
| break; |
| |
| case kEventMouseUp: |
| case kEventMouseDragged: |
| case kEventMouseWheelMoved: |
| HIViewRef view = nullptr; |
| |
| err = HIViewGetViewForMouseEvent(HIViewGetRoot(window), inEvent, &view); |
| if (err == noErr && view && HIObjectIsOfClass((HIObjectRef)view, kHIWebViewClassID)) |
| result = SendEventToEventTargetWithOptions(inEvent, HIObjectGetEventTarget((HIObjectRef)view), kEventTargetDontPropagate); |
| break; |
| } |
| break; |
| } |
| |
| return result; |
| } |
| |
| |
| //---------------------------------------------------------------------------------- |
| // SyncFrame |
| //---------------------------------------------------------------------------------- |
| // |
| static void SyncFrame(HIWebView* inView) |
| { |
| HIViewRef parent = HIViewGetSuperview(inView->fViewRef); |
| |
| if (parent) { |
| if (inView->fIsComposited) { |
| HIRect frame; |
| HIRect parentBounds; |
| NSPoint origin; |
| |
| HIViewGetFrame(inView->fViewRef, &frame); |
| HIViewGetBounds(parent, &parentBounds); |
| |
| origin.x = frame.origin.x; |
| origin.y = parentBounds.size.height - CGRectGetMaxY(frame); |
| // printf("syncing to (%g %g) (%g %g)\n", origin.x, origin.y, |
| // frame.size.width, frame.size.height); |
| [inView->fWebView setFrameOrigin: origin]; |
| [inView->fWebView setFrameSize: *(NSSize*)&frame.size]; |
| } else { |
| GrafPtr port = GetWindowPort(GetControlOwner(inView->fViewRef)); |
| PixMapHandle portPix = GetPortPixMap(port); |
| Rect bounds; |
| HIRect rootFrame; |
| HIRect frame; |
| |
| GetControlBounds(inView->fViewRef, &bounds); |
| OffsetRect(&bounds, -(**portPix).bounds.left, -(**portPix).bounds.top); |
| |
| // printf("control lives at %d %d %d %d in window-coords\n", bounds.top, bounds.left, |
| // bounds.bottom, bounds.right); |
| |
| HIViewGetFrame(HIViewGetRoot(GetControlOwner(inView->fViewRef)), &rootFrame); |
| |
| frame.origin.x = bounds.left; |
| frame.origin.y = rootFrame.size.height - bounds.bottom; |
| frame.size.width = bounds.right - bounds.left; |
| frame.size.height = bounds.bottom - bounds.top; |
| |
| // printf(" before frame convert (%g %g) (%g %g)\n", frame.origin.x, frame.origin.y, |
| // frame.size.width, frame.size.height); |
| |
| [inView->fWebView convertRect:*(NSRect*)&frame fromView:nil]; |
| |
| // printf(" moving web view to (%g %g) (%g %g)\n", frame.origin.x, frame.origin.y, |
| // frame.size.width, frame.size.height); |
| |
| [inView->fWebView setFrameOrigin: *(NSPoint*)&frame.origin]; |
| [inView->fWebView setFrameSize: *(NSSize*)&frame.size]; |
| } |
| } |
| } |
| |
| //---------------------------------------------------------------------------------- |
| // SetFocusPart |
| //---------------------------------------------------------------------------------- |
| // |
| static OSStatus SetFocusPart(HIWebView* view, ControlPartCode desiredFocus, RgnHandle invalidRgn, Boolean focusEverything, ControlPartCode* actualFocus) |
| { |
| NSView *freshlyMadeFirstResponderView; |
| SInt32 partCodeToReturn; |
| |
| // Do what Carbon is telling us to do. |
| if (desiredFocus == kControlFocusNoPart) { |
| // Relinquish the keyboard focus. |
| RelinquishFocus(view, true); // (autodisplay ? YES : NO)); |
| freshlyMadeFirstResponderView = nil; |
| partCodeToReturn = kControlFocusNoPart; |
| // NSLog(@"Relinquished the key focus because we have no choice."); |
| } else if (desiredFocus == kControlFocusNextPart || desiredFocus == kControlFocusPrevPart) { |
| BOOL goForward = (desiredFocus == kControlFocusNextPart); |
| |
| // Advance the keyboard focus, maybe right off of this view. Maybe a subview of this one already has the keyboard focus, maybe not. |
| freshlyMadeFirstResponderView = AdvanceFocus(view, goForward); |
| if (freshlyMadeFirstResponderView) |
| partCodeToReturn = desiredFocus; |
| else |
| partCodeToReturn = kControlFocusNoPart; |
| // NSLog(freshlyMadeFirstResponderView ? @"Advanced the key focus." : @"Relinquished the key focus."); |
| } else { |
| // What's this? |
| if (desiredFocus != kControlIndicatorPart) |
| ASSERT_NOT_REACHED(); |
| freshlyMadeFirstResponderView = nil; |
| partCodeToReturn = desiredFocus; |
| } |
| |
| view->fFirstResponder = freshlyMadeFirstResponderView; |
| |
| *actualFocus = partCodeToReturn; |
| |
| // Done. |
| return noErr; |
| } |
| |
| //---------------------------------------------------------------------------------- |
| // AdvanceFocus |
| //---------------------------------------------------------------------------------- |
| // |
| static NSView* AdvanceFocus(HIWebView* view, bool forward) |
| { |
| NSResponder* oldFirstResponder; |
| NSView* currentKeyView; |
| NSView* viewWeMadeFirstResponder; |
| |
| // Focus on some part (subview) of this control (view). |
| // Maybe a subview of this one already has the keyboard focus, maybe not. |
| |
| oldFirstResponder = [view->fKitWindow firstResponder]; |
| |
| // If we tab out of our NSView, it will no longer be the responder |
| // when we get here. We'll try this trick for now. We might need to |
| // tag the view appropriately. |
| |
| if (view->fFirstResponder && ((NSResponder*)view->fFirstResponder != oldFirstResponder)) |
| return nullptr; |
| |
| if ([oldFirstResponder isKindOfClass:[NSView class]]) { |
| NSView* tentativeNewKeyView; |
| |
| // Some view in this window already has the keyboard focus. It better at least be a subview of this one. |
| NSView* oldFirstResponderView = (NSView *)oldFirstResponder; |
| ASSERT([oldFirstResponderView isDescendantOf:view->fWebView]); |
| |
| if (oldFirstResponderView != view->fFirstResponder && ![oldFirstResponderView isDescendantOf:view->fFirstResponder]) { |
| // Despite our efforts to record what view we made the first responder |
| // (for use in the next paragraph) we couldn't keep up because the user |
| // has clicked in a text field to make it the key focus, instead of using |
| // the tab key. Find a control on which it's reasonable to invoke |
| // -[NSView nextValidKeyView], taking into account the fact that |
| // NSTextFields always pass on first-respondership to a temporarily- |
| // contained NSTextView. |
| |
| NSView *viewBeingTested; |
| currentKeyView = oldFirstResponderView; |
| viewBeingTested = currentKeyView; |
| while (viewBeingTested != view->fWebView) { |
| if ([viewBeingTested isKindOfClass:[NSTextField class]]) { |
| currentKeyView = viewBeingTested; |
| break; |
| } |
| viewBeingTested = [viewBeingTested superview]; |
| } |
| } else { |
| // We recorded which view we made into the first responder the |
| // last time the user hit the tab key, and nothing has invalidated |
| // our recorded value since. |
| |
| currentKeyView = view->fFirstResponder; |
| } |
| |
| // Try to move on to the next or previous key view. We use the laboriously |
| // recorded/figured currentKeyView instead of just oldFirstResponder as the |
| // jumping-off-point when searching for the next valid key view. This is so |
| // we don't get fooled if we recently made some view the first responder, but |
| // it passed on first-responder-ness to some temporary subview. |
| // |
| // You can't put normal views in a window with Carbon-control-wrapped views. |
| // Stuff like this would break. M.P. Notice - 12/2/00 |
| |
| tentativeNewKeyView = forward ? [currentKeyView nextValidKeyView] : [currentKeyView previousValidKeyView]; |
| if (tentativeNewKeyView && [tentativeNewKeyView isDescendantOf:view->fWebView]) { |
| // The user has tabbed to another subview of this control view. Change the keyboard focus. |
| //NSLog(@"Tabbed to the next or previous key view."); |
| |
| [view->fKitWindow makeFirstResponder:tentativeNewKeyView]; |
| viewWeMadeFirstResponder = tentativeNewKeyView; |
| } else { |
| // The user has tabbed past the subviews of this control view. The window is the first responder now. |
| //NSLog(@"Tabbed past the first or last key view."); |
| [view->fKitWindow makeFirstResponder:view->fKitWindow]; |
| viewWeMadeFirstResponder = nil; |
| } |
| } else { |
| // No view in this window has the keyboard focus. This view should |
| // try to select one of its key subviews. We're not interested in |
| // the subviews of sibling views here. |
| |
| // NSLog(@"No keyboard focus in window. Attempting to set..."); |
| |
| NSView *tentativeNewKeyView; |
| ASSERT(oldFirstResponder == view->fKitWindow); |
| if ([view->fWebView acceptsFirstResponder]) |
| tentativeNewKeyView = view->fWebView; |
| else |
| tentativeNewKeyView = [view->fWebView nextValidKeyView]; |
| if (tentativeNewKeyView && [tentativeNewKeyView isDescendantOf:view->fWebView]) { |
| // This control view has at least one subview that can take the keyboard focus. |
| if (!forward) { |
| // The user has tabbed into this control view backwards. Find |
| // and select the last subview of this one that can take the |
| // keyboard focus. Watch out for loops of valid key views. |
| |
| NSView *firstTentativeNewKeyView = tentativeNewKeyView; |
| NSView *nextTentativeNewKeyView = [tentativeNewKeyView nextValidKeyView]; |
| while (nextTentativeNewKeyView && [nextTentativeNewKeyView isDescendantOf:view->fWebView] && nextTentativeNewKeyView != firstTentativeNewKeyView) { |
| tentativeNewKeyView = nextTentativeNewKeyView; |
| nextTentativeNewKeyView = [tentativeNewKeyView nextValidKeyView]; |
| } |
| |
| } |
| |
| // Set the keyboard focus. |
| //NSLog(@"Tabbed into the first or last key view."); |
| [view->fKitWindow makeFirstResponder:tentativeNewKeyView]; |
| viewWeMadeFirstResponder = tentativeNewKeyView; |
| } else { |
| // This control view has no subviews that can take the keyboard focus. |
| //NSLog(@"Can't tab into this view."); |
| viewWeMadeFirstResponder = nil; |
| } |
| } |
| |
| // Done. |
| return viewWeMadeFirstResponder; |
| } |
| |
| |
| //---------------------------------------------------------------------------------- |
| // RelinquishFocus |
| //---------------------------------------------------------------------------------- |
| // |
| static void RelinquishFocus(HIWebView* view, bool inAutodisplay) |
| { |
| NSResponder* firstResponder; |
| |
| // Apparently Carbon thinks that some subview of this control view has the keyboard focus, |
| // or we wouldn't be being asked to relinquish focus. |
| |
| firstResponder = [view->fKitWindow firstResponder]; |
| if ([firstResponder isKindOfClass:[NSView class]]) { |
| // Some subview of this control view really is the first responder right now. |
| ASSERT([(NSView *)firstResponder isDescendantOf:view->fWebView]); |
| |
| // Make the window the first responder, so that no view is the key view. |
| [view->fKitWindow makeFirstResponder:view->fKitWindow]; |
| |
| // If this control is not allowed to do autodisplay, don't let |
| // it autodisplay any just-changed focus rings or text on the |
| // next go around the event loop. I'm probably clearing more |
| // dirty rects than I have to, but it doesn't seem to hurt in |
| // the print panel accessory view case, and I don't have time |
| // to figure out exactly what -[NSCell _setKeyboardFocusRingNeedsDisplay] |
| // is doing when invoked indirectly from -makeFirstResponder up above. M.P. Notice - 12/4/00 |
| |
| if (!inAutodisplay) |
| [[view->fWebView opaqueAncestor] _clearDirtyRectsForTree]; |
| } else { |
| // The Cocoa first responder does not correspond to the Carbon |
| // control that has the keyboard focus. This can happen when |
| // you've closed a dialog by hitting return in an NSTextView |
| // that's a subview of this one; Cocoa closed the window, and |
| // now Carbon is telling this control to relinquish the focus |
| // as it's being disposed. There's nothing to do. |
| |
| ASSERT(firstResponder == view->fKitWindow); |
| } |
| } |
| |
| //---------------------------------------------------------------------------------- |
| // ActiveStateChanged |
| //---------------------------------------------------------------------------------- |
| // |
| static void ActiveStateChanged(HIWebView* view) |
| { |
| if ([view->fWebView respondsToSelector:@selector(setEnabled)]) { |
| [(NSControl*)view->fWebView setEnabled: IsControlEnabled(view->fViewRef)]; |
| HIViewSetNeedsDisplay(view->fViewRef, true); |
| } |
| } |
| |
| |
| //---------------------------------------------------------------------------------- |
| // ProcessCommand |
| //---------------------------------------------------------------------------------- |
| // |
| static OSStatus ProcessCommand(HIWebView* inView, const HICommand* inCommand) |
| { |
| OSStatus result = eventNotHandledErr; |
| NSResponder* resp; |
| |
| resp = [inView->fKitWindow firstResponder]; |
| |
| if ([resp isKindOfClass:[NSView class]]) { |
| NSView* respView = (NSView*)resp; |
| |
| if (respView == inView->fWebView || [respView isDescendantOf: inView->fWebView]) { |
| switch (inCommand->commandID) { |
| case kHICommandCut: |
| case kHICommandCopy: |
| case kHICommandPaste: |
| case kHICommandClear: |
| case kHICommandSelectAll: { |
| SEL selector = _NSSelectorForHICommand(inCommand); |
| if ([respView respondsToSelector:selector]) { |
| [respView performSelector:selector withObject:nil]; |
| result = noErr; |
| } |
| break; |
| } |
| } |
| } |
| } |
| |
| return result; |
| } |
| |
| //---------------------------------------------------------------------------------- |
| // UpdateCommandStatus |
| //---------------------------------------------------------------------------------- |
| // |
| static OSStatus |
| UpdateCommandStatus(HIWebView* inView, const HICommand* inCommand) |
| { |
| OSStatus result = eventNotHandledErr; |
| MenuItemProxy* proxy = nullptr; |
| NSResponder* resp; |
| |
| resp = [inView->fKitWindow firstResponder]; |
| |
| if ([resp isKindOfClass:[NSView class]]) { |
| NSView* respView = (NSView*)resp; |
| |
| if ((respView == inView->fWebView || [respView isDescendantOf: inView->fWebView]) && inCommand->attributes & kHICommandFromMenu) { |
| SEL selector = _NSSelectorForHICommand(inCommand); |
| |
| if (selector && [resp respondsToSelector: selector]) { |
| proxy = [[MenuItemProxy alloc] initWithAction: selector]; |
| |
| // Can't use -performSelector:withObject: here because the method we're calling returns BOOL, while |
| // -performSelector:withObject:'s return value is assumed to be an id. |
| if (wtfObjcMsgSend<BOOL>(resp, @selector(validateUserInterfaceItem:), proxy)) |
| EnableMenuItem(inCommand->menu.menuRef, inCommand->menu.menuItemIndex); |
| else |
| DisableMenuItem(inCommand->menu.menuRef, inCommand->menu.menuItemIndex); |
| |
| result = noErr; |
| } |
| } |
| } |
| |
| if (proxy) |
| [proxy release]; |
| |
| return result; |
| } |
| |
| // Blatantly stolen from AppKit and cropped a bit |
| |
| //---------------------------------------------------------------------------------- |
| // _NSSelectorForHICommand |
| //---------------------------------------------------------------------------------- |
| // |
| static SEL _NSSelectorForHICommand(const HICommand* inCommand) |
| { |
| switch (inCommand->commandID) { |
| case kHICommandUndo: |
| return @selector(undo:); |
| case kHICommandRedo: |
| return @selector(redo:); |
| case kHICommandCut: |
| return @selector(cut:); |
| case kHICommandCopy: |
| return @selector(copy:); |
| case kHICommandPaste: |
| return @selector(paste:); |
| case kHICommandClear: |
| return @selector(delete:); |
| case kHICommandSelectAll: |
| return @selector(selectAll:); |
| default: |
| return nullptr; |
| } |
| |
| return nullptr; |
| } |
| |
| |
| //----------------------------------------------------------------------------------- |
| // HIWebViewEventHandler |
| //----------------------------------------------------------------------------------- |
| // Our object's virtual event handler method. I'm not sure if we need this these days. |
| // We used to do various things with it, but those days are long gone... |
| // |
| static OSStatus |
| HIWebViewEventHandler(EventHandlerCallRef inCallRef, EventRef inEvent, void *inUserData) |
| { |
| OSStatus result = eventNotHandledErr; |
| HIPoint where; |
| OSType tag; |
| void *ptr; |
| Size size; |
| UInt32 features; |
| RgnHandle region = nullptr; |
| ControlPartCode part; |
| HIWebView* view = (HIWebView*)inUserData; |
| |
| // [NSApp setWindowsNeedUpdate:YES] must be called before events so that ActivateTSMDocument is called to set an active document. |
| // Without an active document, TSM will use a default document which uses a bottom-line input window which we don't want. |
| [NSApp setWindowsNeedUpdate:YES]; |
| |
| switch (GetEventClass(inEvent)) { |
| case kEventClassHIObject: |
| switch (GetEventKind(inEvent)) { |
| case kEventHIObjectConstruct: |
| HIObjectRef object; |
| |
| result = GetEventParameter(inEvent, kEventParamHIObjectInstance, |
| typeHIObjectRef, nullptr, sizeof(HIObjectRef), nullptr, &object); |
| ASSERT(!result); |
| if (result) |
| return result; |
| |
| // on entry for our construct event, we're passed the |
| // creation proc we registered with for this class. |
| // we use it now to create the instance, and then we |
| // replace the instance parameter data with said instance |
| // as type void. |
| |
| view = HIWebViewConstructor((HIViewRef)object); |
| |
| if (view) |
| SetEventParameter(inEvent, kEventParamHIObjectInstance, typeVoidPtr, sizeof(void *), &view); |
| break; |
| |
| case kEventHIObjectDestruct: |
| HIWebViewDestructor(view); |
| // result is unimportant |
| break; |
| } |
| break; |
| |
| case kEventClassKeyboard: { |
| NSEvent* kitEvent = CreateNSEventWithCarbonEvent(inEvent); |
| [view->fKitWindow sendSuperEvent:kitEvent]; |
| [kitEvent release]; |
| result = noErr; |
| break; |
| } |
| |
| case kEventClassMouse: |
| switch (GetEventKind(inEvent)) { |
| case kEventMouseUp: |
| result = MouseUp(view, inEvent); |
| break; |
| |
| case kEventMouseWheelMoved: |
| result = MouseWheelMoved(view, inEvent); |
| break; |
| |
| case kEventMouseMoved: |
| result = MouseMoved(view, inEvent); |
| break; |
| |
| case kEventMouseDragged: |
| result = MouseDragged(view, inEvent); |
| break; |
| } |
| break; |
| |
| case kEventClassCommand: |
| HICommand command; |
| |
| result = GetEventParameter(inEvent, kEventParamDirectObject, typeHICommand, nullptr, sizeof(HICommand), nullptr, &command); |
| ASSERT(!result); |
| if (result) |
| return result; |
| |
| switch (GetEventKind(inEvent)) { |
| case kEventCommandProcess: |
| result = ProcessCommand(view, &command); |
| break; |
| |
| case kEventCommandUpdateStatus: |
| result = UpdateCommandStatus(view, &command); |
| break; |
| } |
| break; |
| |
| case kEventClassControl: |
| switch (GetEventKind(inEvent)) { |
| case kEventControlInitialize: |
| features = GetBehaviors(); |
| SetEventParameter(inEvent, kEventParamControlFeatures, typeUInt32, sizeof(UInt32), &features); |
| result = noErr; |
| break; |
| |
| case kEventControlDraw: { |
| CGContextRef context = nullptr; |
| |
| GetEventParameter(inEvent, kEventParamRgnHandle, typeQDRgnHandle, nullptr, sizeof(RgnHandle), nullptr, ®ion); |
| GetEventParameter(inEvent, kEventParamCGContextRef, typeCGContextRef, nullptr, sizeof(CGContextRef), nullptr, &context); |
| |
| Draw(view, region, context); |
| result = noErr; |
| break; |
| } |
| |
| case kEventControlHitTest: |
| GetEventParameter(inEvent, kEventParamMouseLocation, typeHIPoint, nullptr, sizeof(HIPoint), nullptr, &where); |
| part = HitTest(view, &where); |
| SetEventParameter(inEvent, kEventParamControlPart, typeControlPartCode, sizeof(ControlPartCode), &part); |
| result = noErr; |
| break; |
| |
| case kEventControlGetPartRegion: |
| GetEventParameter(inEvent, kEventParamControlPart, typeControlPartCode, nullptr, sizeof(ControlPartCode), nullptr, &part); |
| GetEventParameter(inEvent, kEventParamControlRegion, typeQDRgnHandle, nullptr, sizeof(RgnHandle), nullptr, ®ion); |
| result = GetRegion(view, part, region); |
| break; |
| |
| case kEventControlGetData: { |
| GetEventParameter(inEvent, kEventParamControlPart, typeControlPartCode, nullptr, sizeof(ControlPartCode), nullptr, &part); |
| GetEventParameter(inEvent, kEventParamControlDataTag, typeEnumeration, nullptr, sizeof(OSType), nullptr, &tag); |
| GetEventParameter(inEvent, kEventParamControlDataBuffer, typePtr, nullptr, sizeof(Ptr), nullptr, &ptr); |
| GetEventParameter(inEvent, kEventParamControlDataBufferSize, typeByteCount, nullptr, sizeof(Size), nullptr, &size); |
| |
| if (tag == kControlKindTag) { |
| Size outSize; |
| result = noErr; |
| |
| if (ptr) { |
| if (size != sizeof(ControlKind)) |
| result = errDataSizeMismatch; |
| else |
| (*(ControlKind *)ptr) = GetKind(); |
| } |
| |
| outSize = sizeof(ControlKind); |
| SetEventParameter(inEvent, kEventParamControlDataBufferSize, typeByteCount, sizeof(Size), &outSize); |
| } |
| break; |
| } |
| |
| case kEventControlBoundsChanged: { |
| HIRect prevRect, currRect; |
| UInt32 attrs; |
| |
| GetEventParameter(inEvent, kEventParamAttributes, typeUInt32, nullptr, sizeof(UInt32), nullptr, &attrs); |
| GetEventParameter(inEvent, kEventParamOriginalBounds, typeHIRect, nullptr, sizeof(HIRect), nullptr, &prevRect); |
| GetEventParameter(inEvent, kEventParamCurrentBounds, typeHIRect, nullptr, sizeof(HIRect), nullptr, &currRect); |
| |
| BoundsChanged(view, attrs, &prevRect, &currRect); |
| result = noErr; |
| break; |
| } |
| |
| case kEventControlActivate: |
| ActiveStateChanged(view); |
| result = noErr; |
| break; |
| |
| case kEventControlDeactivate: |
| ActiveStateChanged(view); |
| result = noErr; |
| break; |
| |
| case kEventControlOwningWindowChanged: { |
| WindowRef fromWindow, toWindow; |
| |
| result = GetEventParameter(inEvent, kEventParamControlOriginalOwningWindow, typeWindowRef, nullptr, sizeof(WindowRef), nullptr, &fromWindow); |
| ASSERT(!result); |
| if (result) |
| return result; |
| |
| result = GetEventParameter(inEvent, kEventParamControlCurrentOwningWindow, typeWindowRef, nullptr, sizeof(WindowRef), nullptr, &toWindow); |
| ASSERT(!result); |
| if (result) |
| return result; |
| |
| OwningWindowChanged(view, fromWindow, toWindow); |
| result = noErr; |
| break; |
| } |
| |
| case kEventControlClick: |
| result = Click(view, inEvent); |
| break; |
| |
| case kEventControlContextualMenuClick: |
| result = ContextMenuClick(view, inEvent); |
| break; |
| |
| case kEventControlSetFocusPart: { |
| ControlPartCode desiredFocus; |
| RgnHandle invalidRgn; |
| Boolean focusEverything; |
| ControlPartCode actualFocus; |
| |
| result = GetEventParameter(inEvent, kEventParamControlPart, typeControlPartCode, nullptr, sizeof(ControlPartCode), nullptr, &desiredFocus); |
| ASSERT(!result); |
| if (result) |
| return result; |
| |
| GetEventParameter(inEvent, kEventParamControlInvalRgn, typeQDRgnHandle, nullptr, sizeof(RgnHandle), nullptr, &invalidRgn); |
| focusEverything = false; // a good default in case the parameter doesn't exist |
| GetEventParameter(inEvent, kEventParamControlFocusEverything, typeBoolean, nullptr, sizeof(Boolean), nullptr, &focusEverything); |
| |
| result = SetFocusPart(view, desiredFocus, invalidRgn, focusEverything, &actualFocus); |
| if (result == noErr) |
| ASSERT(!SetEventParameter(inEvent, kEventParamControlPart, typeControlPartCode, sizeof(ControlPartCode), &actualFocus)); |
| break; |
| } |
| |
| // some other kind of Control event |
| default: |
| break; |
| } |
| break; |
| |
| // some other event class |
| default: |
| break; |
| } |
| return result; |
| } |
| |
| |
| static void UpdateObserver(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info); |
| |
| static void StartUpdateObserver(HIWebView* view) |
| { |
| CFRunLoopObserverContext context; |
| CFRunLoopObserverRef observer; |
| CFRunLoopRef mainRunLoop; |
| |
| ASSERT(!view->fIsComposited); |
| ASSERT(!view->fUpdateObserver); |
| |
| context.version = 0; |
| context.info = view; |
| context.retain = nullptr; |
| context.release = nullptr; |
| context.copyDescription = nullptr; |
| |
| mainRunLoop = checked_cf_cast<CFRunLoopRef>(GetCFRunLoopFromEventLoop(GetMainEventLoop())); |
| observer = CFRunLoopObserverCreate(nullptr, kCFRunLoopEntry | kCFRunLoopBeforeWaiting, true, 0, UpdateObserver, &context); |
| CFRunLoopAddObserver(mainRunLoop, observer, kCFRunLoopCommonModes); |
| |
| view->fUpdateObserver = observer; |
| |
| // printf("Update observer started\n"); |
| } |
| |
| static void StopUpdateObserver(HIWebView* view) |
| { |
| ASSERT(!view->fIsComposited); |
| ASSERT(view->fUpdateObserver); |
| |
| CFRunLoopObserverInvalidate(view->fUpdateObserver); |
| CFRelease(view->fUpdateObserver); |
| view->fUpdateObserver = nullptr; |
| |
| // printf("Update observer removed\n"); |
| } |
| |
| static void |
| UpdateObserver(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info) |
| { |
| HIWebView* view = (HIWebView*)info; |
| RgnHandle region = NewRgn(); |
| |
| // printf("Update observer called\n"); |
| |
| if (region) { |
| GetWindowRegion(GetControlOwner(view->fViewRef), kWindowUpdateRgn, region); |
| |
| if (!EmptyRgn(region)) { |
| RgnHandle ourRgn = NewRgn(); |
| Rect rect; |
| |
| GetWindowBounds(GetControlOwner(view->fViewRef), kWindowStructureRgn, &rect); |
| |
| // printf("Update region is non-empty\n"); |
| if (ourRgn) { |
| Rect rect; |
| GrafPtr savePort, port; |
| Point offset = { 0, 0 }; |
| |
| port = GetWindowPort(GetControlOwner(view->fViewRef)); |
| |
| GetPort(&savePort); |
| SetPort(port); |
| |
| GlobalToLocal(&offset); |
| OffsetRgn(region, offset.h, offset.v); |
| |
| GetControlBounds(view->fViewRef, &rect); |
| RectRgn(ourRgn, &rect); |
| |
| // printf("our control is at %d %d %d %d\n", rect.top, rect.left, rect.bottom, rect.right); |
| GetRegionBounds(region, &rect); |
| // printf("region is at %d %d %d %d\n", rect.top, rect.left, rect.bottom, rect.right); |
| |
| SectRgn(ourRgn, region, ourRgn); |
| |
| GetRegionBounds(ourRgn, &rect); |
| // printf("intersection is %d %d %d %d\n", rect.top, rect.left, rect.bottom, rect.right); |
| if (!EmptyRgn(ourRgn)) { |
| RgnHandle saveVis = NewRgn(); |
| |
| // printf("looks like we should draw\n"); |
| if (saveVis) { |
| // RGBColor kRedColor = { 0xffff, 0, 0 }; |
| GetPortVisibleRegion(GetWindowPort(GetControlOwner(view->fViewRef)), saveVis); |
| SetPortVisibleRegion(GetWindowPort(GetControlOwner(view->fViewRef)), ourRgn); |
| |
| // RGBForeColor(&kRedColor); |
| // PaintRgn(ourRgn); |
| // QDFlushPortBuffer(port, nullptr); |
| // Delay(15, nullptr); |
| Draw1Control(view->fViewRef); |
| ValidWindowRgn(GetControlOwner(view->fViewRef), ourRgn); |
| |
| SetPortVisibleRegion(GetWindowPort(GetControlOwner(view->fViewRef)), saveVis); |
| DisposeRgn(saveVis); |
| } |
| } |
| |
| SetPort(savePort); |
| |
| DisposeRgn(ourRgn); |
| } |
| } |
| |
| DisposeRgn(region); |
| } |
| } |
| |
| #endif |