HIWebView.mm   [plain text]


/*
 * 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)
{
    NSWindowGraphicsContext *graphicsContext = (NSWindowGraphicsContext *)window.graphicsContext;
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
    CGContextRef savedContext = (CGContextRef)graphicsContext.graphicsPort;
#pragma clang diagnostic pop
    CGContextRetain(savedContext);
    [graphicsContext _web_setGraphicsPort:context];
    return savedContext;
}

static void restoreCGContext(NSWindow *window, CGContextRef savedContext)
{
    NSWindowGraphicsContext *graphicsContext = (NSWindowGraphicsContext *)window.graphicsContext;
    [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, &region);
            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, &region);
            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