WebKit2/UIProcess/API/mac/WKView.mm
changeset 0 4f2f89ce4247
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebKit2/UIProcess/API/mac/WKView.mm	Fri Sep 17 09:02:29 2010 +0300
@@ -0,0 +1,551 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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.
+ */
+
+#import "WKView.h"
+
+// C API
+#import "WKAPICast.h"
+
+// Implementation
+#import "ChunkedUpdateDrawingAreaProxy.h"
+#import "LayerBackedDrawingAreaProxy.h"
+#import "PageClientImpl.h"
+#import "RunLoop.h"
+#import "WebContext.h"
+#import "WebEventFactory.h"
+#import "WebPage.h"
+#import "WebPageNamespace.h"
+#import "WebPageProxy.h"
+#import "WebProcessManager.h"
+#import "WebProcessProxy.h"
+#import <QuartzCore/QuartzCore.h>
+#import <WebCore/IntRect.h>
+#import <wtf/RefPtr.h>
+
+using namespace WebKit;
+using namespace WebCore;
+
+@interface WKViewData : NSObject {
+@public
+    RefPtr<WebPageProxy> _page;
+
+    // For ToolTips.
+    NSToolTipTag _lastToolTipTag;
+    id _trackingRectOwner;
+    void* _trackingRectUserData;
+
+#if USE(ACCELERATED_COMPOSITING)
+    NSView *_layerHostingView;
+#endif
+}
+@end
+
+@implementation WKViewData
+@end
+
+@implementation WKView
+
+- (id)initWithFrame:(NSRect)frame pageNamespaceRef:(WKPageNamespaceRef)pageNamespaceRef
+{
+    self = [super initWithFrame:frame];
+    if (!self)
+        return nil;
+
+    RunLoop::initializeMainRunLoop();
+
+    NSTrackingArea *trackingArea = [[NSTrackingArea alloc] initWithRect:frame
+                                                                options:(NSTrackingMouseMoved | NSTrackingActiveInKeyWindow | NSTrackingInVisibleRect)
+                                                                  owner:self
+                                                               userInfo:nil];
+    [self addTrackingArea:trackingArea];
+    [trackingArea release];
+
+    _data = [[WKViewData alloc] init];
+
+    _data->_page = toWK(pageNamespaceRef)->createWebPage();
+    _data->_page->setPageClient(new PageClientImpl(self));
+    _data->_page->initializeWebPage(IntSize(frame.size), new ChunkedUpdateDrawingAreaProxy(self));
+    _data->_page->setIsInWindow([self window]);
+
+    return self;
+}
+
+- (id)initWithFrame:(NSRect)frame
+{
+    WebContext* context = WebContext::sharedProcessContext();
+    self = [self initWithFrame:frame pageNamespaceRef:toRef(context->createPageNamespace())];
+    if (!self)
+        return nil;
+
+    return self;
+}
+
+- (void)dealloc
+{
+    _data->_page->close();
+
+    [_data release];
+    [super dealloc];
+}
+
+- (WKPageRef)pageRef
+{
+    return toRef(_data->_page.get());
+}
+
+- (BOOL)acceptsFirstResponder
+{
+    return YES;
+}
+
+- (BOOL)becomeFirstResponder
+{
+    _data->_page->setFocused(true);
+    return YES;
+}
+
+- (BOOL)resignFirstResponder
+{
+    _data->_page->setFocused(false);
+    return YES;
+}
+
+- (BOOL)isFlipped
+{
+    return YES;
+}
+
+- (void)setFrameSize:(NSSize)size
+{
+    [super setFrameSize:size];
+
+    _data->_page->drawingArea()->setSize(IntSize(size));
+}
+
+// Events
+
+- (void)mouseDown:(NSEvent *)theEvent
+{
+    WebMouseEvent mouseEvent = WebEventFactory::createWebMouseEvent(theEvent, self);
+    _data->_page->mouseEvent(mouseEvent);    
+}
+
+- (void)mouseUp:(NSEvent *)theEvent
+{
+    if (!_data->_page->isValid()) {
+        _data->_page->revive();
+        _data->_page->loadURL(_data->_page->urlAtProcessExit());
+        return;
+    }
+
+    WebMouseEvent mouseEvent = WebEventFactory::createWebMouseEvent(theEvent, self);
+    _data->_page->mouseEvent(mouseEvent);
+}
+
+- (void)mouseMoved:(NSEvent *)theEvent
+{
+    WebMouseEvent mouseEvent = WebEventFactory::createWebMouseEvent(theEvent, self);
+    _data->_page->mouseEvent(mouseEvent);
+}
+
+- (void)mouseDragged:(NSEvent *)theEvent
+{
+    WebMouseEvent mouseEvent = WebEventFactory::createWebMouseEvent(theEvent, self);
+    _data->_page->mouseEvent(mouseEvent);
+}
+
+- (void)scrollWheel:(NSEvent *)theEvent
+{
+    WebWheelEvent wheelEvent = WebEventFactory::createWebWheelEvent(theEvent, self);
+    _data->_page->wheelEvent(wheelEvent);
+}
+
+- (void)keyUp:(NSEvent *)theEvent
+{
+    WebKeyboardEvent keyboardEvent = WebEventFactory::createWebKeyboardEvent(theEvent);
+    _data->_page->keyEvent(keyboardEvent);
+}
+
+- (void)keyDown:(NSEvent *)theEvent
+{
+    WebKeyboardEvent keyboardEvent = WebEventFactory::createWebKeyboardEvent(theEvent);
+    _data->_page->keyEvent(keyboardEvent);
+}
+
+- (void)_updateActiveState
+{
+    _data->_page->setActive([[self window] isKeyWindow]);
+}
+
+- (void)addWindowObserversForWindow:(NSWindow *)window
+{
+    if (window) {
+        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowDidBecomeKey:)
+            name:NSWindowDidBecomeKeyNotification object:nil];
+        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowDidResignKey:)
+            name:NSWindowDidResignKeyNotification object:nil];
+    }
+}
+
+- (void)removeWindowObservers
+{
+    NSWindow *window = [self window];
+    if (window) {
+        [[NSNotificationCenter defaultCenter] removeObserver:self
+            name:NSWindowDidBecomeKeyNotification object:nil];
+        [[NSNotificationCenter defaultCenter] removeObserver:self
+            name:NSWindowDidResignKeyNotification object:nil];
+    }
+}
+
+static bool isViewVisible(NSView *view)
+{
+    if (![view window])
+        return false;
+    
+    if ([view isHiddenOrHasHiddenAncestor])
+        return false;
+    
+    return true;
+}
+
+- (void)_updateVisibility
+{
+    _data->_page->setIsInWindow([self window]);
+    _data->_page->drawingArea()->setPageIsVisible(isViewVisible(self));
+}
+
+- (void)viewWillMoveToWindow:(NSWindow *)window
+{
+    if (window != [self window]) {
+        [self removeWindowObservers];
+        [self addWindowObserversForWindow:window];
+    }
+}
+
+- (void)viewDidMoveToWindow
+{
+    // We want to make sure to update the active state while hidden, so if the view is about to become visible, we
+    // update the active state first and then make it visible. If the view is about to be hidden, we hide it first and then
+    // update the active state.
+    if ([self window]) {
+        [self _updateActiveState];
+        [self _updateVisibility];
+    } else {
+        [self _updateVisibility];
+        [self _updateActiveState];
+    }
+}
+
+- (void)_windowDidBecomeKey:(NSNotification *)notification
+{
+    NSWindow *keyWindow = [notification object];
+    if (keyWindow == [self window] || keyWindow == [[self window] attachedSheet])
+        [self _updateActiveState];
+}
+
+- (void)_windowDidResignKey:(NSNotification *)notification
+{
+    NSWindow *formerKeyWindow = [notification object];
+    if (formerKeyWindow == [self window] || formerKeyWindow == [[self window] attachedSheet])
+        [self _updateActiveState];
+}
+
+- (void)drawRect:(NSRect)rect
+{    
+    [[NSColor whiteColor] set];
+    NSRectFill(rect);
+
+    if (_data->_page->isValid() && _data->_page->drawingArea()) {
+        CGContextRef context = static_cast<CGContextRef>([[NSGraphicsContext currentContext] graphicsPort]);
+        _data->_page->drawingArea()->paint(IntRect(rect), context);
+    }
+}
+
+- (BOOL)isOpaque 
+{
+    // FIXME: Return NO if we have a transparent background.
+    return YES;
+}
+
+- (void)viewDidHide
+{
+    [self _updateVisibility];
+}
+
+- (void)viewDidUnhide
+{
+    [self _updateVisibility];
+}
+
+@end
+
+@implementation WKView (Internal)
+
+- (void)_processDidExit
+{
+    [self setNeedsDisplay:YES];
+}
+
+- (void)_processDidRevive
+{
+    _data->_page->reinitializeWebPage(IntSize([self visibleRect].size));
+
+    _data->_page->setActive([[self window] isKeyWindow]);
+    _data->_page->setFocused([[self window] firstResponder] == self);
+    
+    [self setNeedsDisplay:YES];
+}
+
+- (void)_takeFocus:(BOOL)forward
+{
+    if (forward)
+        [[self window] selectKeyViewFollowingView:self];
+    else
+        [[self window] selectKeyViewPrecedingView:self];
+}
+
+- (void)_setCursor:(NSCursor *)cursor
+{
+    if ([NSCursor currentCursor] == cursor)
+        return;
+    [cursor set];
+}
+
+// Any non-zero value will do, but using something recognizable might help us debug some day.
+#define TRACKING_RECT_TAG 0xBADFACE
+
+- (NSTrackingRectTag)addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside
+{
+    ASSERT(_data->_trackingRectOwner == nil);
+    _data->_trackingRectOwner = owner;
+    _data->_trackingRectUserData = data;
+    return TRACKING_RECT_TAG;
+}
+
+- (NSTrackingRectTag)_addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside useTrackingNum:(int)tag
+{
+    ASSERT(tag == 0 || tag == TRACKING_RECT_TAG);
+    ASSERT(_data->_trackingRectOwner == nil);
+    _data->_trackingRectOwner = owner;
+    _data->_trackingRectUserData = data;
+    return TRACKING_RECT_TAG;
+}
+
+- (void)_addTrackingRects:(NSRect *)rects owner:(id)owner userDataList:(void **)userDataList assumeInsideList:(BOOL *)assumeInsideList trackingNums:(NSTrackingRectTag *)trackingNums count:(int)count
+{
+    ASSERT(count == 1);
+    ASSERT(trackingNums[0] == 0 || trackingNums[0] == TRACKING_RECT_TAG);
+    ASSERT(_data->_trackingRectOwner == nil);
+    _data->_trackingRectOwner = owner;
+    _data->_trackingRectUserData = userDataList[0];
+    trackingNums[0] = TRACKING_RECT_TAG;
+}
+
+- (void)removeTrackingRect:(NSTrackingRectTag)tag
+{
+    if (tag == 0)
+        return;
+    
+    if (_data && (tag == TRACKING_RECT_TAG)) {
+        _data->_trackingRectOwner = nil;
+        return;
+    }
+    
+    if (_data && (tag == _data->_lastToolTipTag)) {
+        [super removeTrackingRect:tag];
+        _data->_lastToolTipTag = 0;
+        return;
+    }
+    
+    // If any other tracking rect is being removed, we don't know how it was created
+    // and it's possible there's a leak involved (see 3500217)
+    ASSERT_NOT_REACHED();
+}
+
+- (void)_removeTrackingRects:(NSTrackingRectTag *)tags count:(int)count
+{
+    int i;
+    for (i = 0; i < count; ++i) {
+        int tag = tags[i];
+        if (tag == 0)
+            continue;
+        ASSERT(tag == TRACKING_RECT_TAG);
+        if (_data != nil) {
+            _data->_trackingRectOwner = nil;
+        }
+    }
+}
+
+- (void)_sendToolTipMouseExited
+{
+    // Nothing matters except window, trackingNumber, and userData.
+    NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseExited
+        location:NSMakePoint(0, 0)
+        modifierFlags:0
+        timestamp:0
+        windowNumber:[[self window] windowNumber]
+        context:NULL
+        eventNumber:0
+        trackingNumber:TRACKING_RECT_TAG
+        userData:_data->_trackingRectUserData];
+    [_data->_trackingRectOwner mouseExited:fakeEvent];
+}
+
+- (void)_sendToolTipMouseEntered
+{
+    // Nothing matters except window, trackingNumber, and userData.
+    NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseEntered
+        location:NSMakePoint(0, 0)
+        modifierFlags:0
+        timestamp:0
+        windowNumber:[[self window] windowNumber]
+        context:NULL
+        eventNumber:0
+        trackingNumber:TRACKING_RECT_TAG
+        userData:_data->_trackingRectUserData];
+    [_data->_trackingRectOwner mouseEntered:fakeEvent];
+}
+
+- (NSString *)view:(NSView *)view stringForToolTip:(NSToolTipTag)tag point:(NSPoint)point userData:(void *)data
+{
+    return nsStringFromWebCoreString(_data->_page->toolTip());
+}
+
+- (void)_toolTipChangedFrom:(NSString *)oldToolTip to:(NSString *)newToolTip
+{
+    if (oldToolTip)
+        [self _sendToolTipMouseExited];
+
+    if (newToolTip && [newToolTip length] > 0) {
+        // See radar 3500217 for why we remove all tooltips rather than just the single one we created.
+        [self removeAllToolTips];
+        NSRect wideOpenRect = NSMakeRect(-100000, -100000, 200000, 200000);
+        _data->_lastToolTipTag = [self addToolTipRect:wideOpenRect owner:self userData:NULL];
+        [self _sendToolTipMouseEntered];
+    }
+}
+
+#if USE(ACCELERATED_COMPOSITING)
+- (void)_startAcceleratedCompositing:(CALayer *)rootLayer
+{
+    if (!_data->_layerHostingView) {
+        NSView *hostingView = [[NSView alloc] initWithFrame:[self bounds]];
+#if !defined(BUILDING_ON_LEOPARD)
+        [hostingView setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)];
+#endif
+        
+        [self addSubview:hostingView];
+        [hostingView release];
+        _data->_layerHostingView = hostingView;
+    }
+
+    // Make a container layer, which will get sized/positioned by AppKit and CA.
+    CALayer *viewLayer = [CALayer layer];
+
+#ifndef NDEBUG
+    [viewLayer setName:@"hosting layer"];
+#endif
+
+#if defined(BUILDING_ON_LEOPARD)
+    // Turn off default animations.
+    NSNull *nullValue = [NSNull null];
+    NSDictionary *actions = [NSDictionary dictionaryWithObjectsAndKeys:
+                             nullValue, @"anchorPoint",
+                             nullValue, @"bounds",
+                             nullValue, @"contents",
+                             nullValue, @"contentsRect",
+                             nullValue, @"opacity",
+                             nullValue, @"position",
+                             nullValue, @"sublayerTransform",
+                             nullValue, @"sublayers",
+                             nullValue, @"transform",
+                             nil];
+    [viewLayer setStyle:[NSDictionary dictionaryWithObject:actions forKey:@"actions"]];
+#endif
+
+#if !defined(BUILDING_ON_LEOPARD)
+    // If we aren't in the window yet, we'll use the screen's scale factor now, and reset the scale 
+    // via -viewDidMoveToWindow.
+    CGFloat scaleFactor = [self window] ? [[self window] userSpaceScaleFactor] : [[NSScreen mainScreen] userSpaceScaleFactor];
+    [viewLayer setTransform:CATransform3DMakeScale(scaleFactor, scaleFactor, 1)];
+#endif
+
+    [_data->_layerHostingView setLayer:viewLayer];
+    [_data->_layerHostingView setWantsLayer:YES];
+    
+    // Parent our root layer in the container layer
+    [viewLayer addSublayer:rootLayer];
+}
+
+- (void)_stopAcceleratedCompositing
+{
+    if (_data->_layerHostingView) {
+        [_data->_layerHostingView setLayer:nil];
+        [_data->_layerHostingView setWantsLayer:NO];
+        [_data->_layerHostingView removeFromSuperview];
+        _data->_layerHostingView = nil;
+    }
+}
+
+- (void)_switchToDrawingAreaTypeIfNecessary:(DrawingAreaProxy::Type)type
+{
+    DrawingAreaProxy::Type existingDrawingAreaType = _data->_page->drawingArea() ? _data->_page->drawingArea()->type() : DrawingAreaProxy::None;
+    if (existingDrawingAreaType == type)
+        return;
+
+    OwnPtr<DrawingAreaProxy> newDrawingArea;
+    switch (type) {
+        case DrawingAreaProxy::None:
+            break;
+        case DrawingAreaProxy::ChunkedUpdateDrawingAreaType: {
+            newDrawingArea = new ChunkedUpdateDrawingAreaProxy(self);
+            break;
+        }
+        case DrawingAreaProxy::LayerBackedDrawingAreaType: {
+            newDrawingArea = new LayerBackedDrawingAreaProxy(self);
+            break;
+        }
+    }
+
+    newDrawingArea->setSize(IntSize([self frame].size));
+
+    _data->_page->drawingArea()->detachCompositingContext();
+    _data->_page->setDrawingArea(newDrawingArea.release());
+}
+
+- (void)_pageDidEnterAcceleratedCompositing
+{
+    [self _switchToDrawingAreaTypeIfNecessary:DrawingAreaProxy::LayerBackedDrawingAreaType];
+}
+
+- (void)_pageDidLeaveAcceleratedCompositing
+{
+    // FIXME: we may want to avoid flipping back to the non-layer-backed drawing area until the next page load, to avoid thrashing.
+    [self _switchToDrawingAreaTypeIfNecessary:DrawingAreaProxy::ChunkedUpdateDrawingAreaType];
+}
+#endif // USE(ACCELERATED_COMPOSITING)
+
+@end