2010-07-01 Simon Fraser <simon.fraser@apple.com>
[WebKit-https.git] / WebKit2 / UIProcess / API / mac / WKView.mm
1 /*
2  * Copyright (C) 2010 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #import "WKView.h"
27
28 // C API
29 #import "WKAPICast.h"
30
31 // Implementation
32 #import "ChunkedUpdateDrawingAreaProxy.h"
33 #import "LayerBackedDrawingAreaProxy.h"
34 #import "PageClientImpl.h"
35 #import "RunLoop.h"
36 #import "WebContext.h"
37 #import "WebEventFactory.h"
38 #import "WebPage.h"
39 #import "WebPageNamespace.h"
40 #import "WebPageProxy.h"
41 #import "WebProcessManager.h"
42 #import "WebProcessProxy.h"
43 #import <QuartzCore/QuartzCore.h>
44 #import <WebCore/IntRect.h>
45 #import <wtf/RefPtr.h>
46
47 using namespace WebKit;
48 using namespace WebCore;
49
50 @interface WKViewData : NSObject {
51 @public
52     RefPtr<WebPageProxy> _page;
53
54     // For ToolTips.
55     NSToolTipTag _lastToolTipTag;
56     id _trackingRectOwner;
57     void* _trackingRectUserData;
58
59 #if USE(ACCELERATED_COMPOSITING)
60     NSView* _layerHostingView;
61 #endif
62 }
63 @end
64
65 @implementation WKViewData
66 @end
67
68 @implementation WKView
69
70 - (id)initWithFrame:(NSRect)frame pageNamespaceRef:(WKPageNamespaceRef)pageNamespaceRef
71 {
72     self = [super initWithFrame:frame];
73     if (!self)
74         return nil;
75
76     RunLoop::initializeMainRunLoop();
77
78     NSTrackingArea *trackingArea = [[NSTrackingArea alloc] initWithRect:frame
79                                                                 options:(NSTrackingMouseMoved | NSTrackingActiveInKeyWindow | NSTrackingInVisibleRect)
80                                                                   owner:self
81                                                                userInfo:nil];
82     [self addTrackingArea:trackingArea];
83     [trackingArea release];
84
85     _data = [[WKViewData alloc] init];
86
87     _data->_page = toWK(pageNamespaceRef)->createWebPage();
88     _data->_page->setPageClient(new PageClientImpl(self));
89     _data->_page->initializeWebPage(IntSize(frame.size), new ChunkedUpdateDrawingAreaProxy(self));
90
91     return self;
92 }
93
94 - (id)initWithFrame:(NSRect)frame
95 {
96     WebContext* context = WebContext::sharedProcessContext();
97     self = [self initWithFrame:frame pageNamespaceRef:toRef(context->createPageNamespace())];
98     if (!self)
99         return nil;
100
101     return self;
102 }
103
104 - (void)dealloc
105 {
106     _data->_page->close();
107
108     [_data release];
109     [super dealloc];
110 }
111
112 - (WKPageRef)pageRef
113 {
114     return toRef(_data->_page.get());
115 }
116
117 - (BOOL)acceptsFirstResponder
118 {
119     return YES;
120 }
121
122 - (BOOL)becomeFirstResponder
123 {
124     _data->_page->setFocused(true);
125     return YES;
126 }
127
128 - (BOOL)resignFirstResponder
129 {
130     _data->_page->setFocused(false);
131     return YES;
132 }
133
134 - (BOOL)isFlipped
135 {
136     return YES;
137 }
138
139 - (void)setFrame:(NSRect)frame
140 {
141     [super setFrame:frame];
142
143     _data->_page->drawingArea()->setSize(IntSize(frame.size));
144 }
145
146 // Events
147
148 - (void)mouseDown:(NSEvent *)theEvent
149 {
150     WebMouseEvent mouseEvent = WebEventFactory::createWebMouseEvent(theEvent, self);
151     _data->_page->mouseEvent(mouseEvent);    
152 }
153
154 - (void)mouseUp:(NSEvent *)theEvent
155 {
156     if (!_data->_page->isValid()) {
157         _data->_page->revive();
158         _data->_page->loadURL(_data->_page->urlAtProcessExit());
159         return;
160     }
161
162     WebMouseEvent mouseEvent = WebEventFactory::createWebMouseEvent(theEvent, self);
163     _data->_page->mouseEvent(mouseEvent);
164 }
165
166 - (void)mouseMoved:(NSEvent *)theEvent
167 {
168     WebMouseEvent mouseEvent = WebEventFactory::createWebMouseEvent(theEvent, self);
169     _data->_page->mouseEvent(mouseEvent);
170 }
171
172 - (void)mouseDragged:(NSEvent *)theEvent
173 {
174     WebMouseEvent mouseEvent = WebEventFactory::createWebMouseEvent(theEvent, self);
175     _data->_page->mouseEvent(mouseEvent);
176 }
177
178 - (void)scrollWheel:(NSEvent *)theEvent
179 {
180     WebWheelEvent wheelEvent = WebEventFactory::createWebWheelEvent(theEvent, self);
181     _data->_page->wheelEvent(wheelEvent);
182 }
183
184 - (void)keyUp:(NSEvent *)theEvent
185 {
186     WebKeyboardEvent keyboardEvent = WebEventFactory::createWebKeyboardEvent(theEvent);
187     _data->_page->keyEvent(keyboardEvent);
188 }
189
190 - (void)keyDown:(NSEvent *)theEvent
191 {
192     WebKeyboardEvent keyboardEvent = WebEventFactory::createWebKeyboardEvent(theEvent);
193     _data->_page->keyEvent(keyboardEvent);
194 }
195
196 - (void)_updateActiveState
197 {
198     _data->_page->setActive([[self window] isKeyWindow]);
199 }
200
201 - (void)addWindowObserversForWindow:(NSWindow *)window
202 {
203     if (window) {
204         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowDidBecomeKey:)
205             name:NSWindowDidBecomeKeyNotification object:nil];
206         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowDidResignKey:)
207             name:NSWindowDidResignKeyNotification object:nil];
208     }
209 }
210
211 - (void)removeWindowObservers
212 {
213     NSWindow *window = [self window];
214     if (window) {
215         [[NSNotificationCenter defaultCenter] removeObserver:self
216             name:NSWindowDidBecomeKeyNotification object:nil];
217         [[NSNotificationCenter defaultCenter] removeObserver:self
218             name:NSWindowDidResignKeyNotification object:nil];
219     }
220 }
221
222 static bool isViewVisible(NSView *view)
223 {
224     if (![view window])
225         return false;
226     
227     if ([view isHiddenOrHasHiddenAncestor])
228         return false;
229     
230     return true;
231 }
232
233 - (void)_updateVisibility
234 {
235     _data->_page->drawingArea()->setPageIsVisible(isViewVisible(self));
236 }
237
238 - (void)viewWillMoveToWindow:(NSWindow *)window
239 {
240     if (window != [self window]) {
241         [self removeWindowObservers];
242         [self addWindowObserversForWindow:window];
243     }
244 }
245
246 - (void)viewDidMoveToWindow
247 {
248     [self _updateVisibility];
249     [self _updateActiveState];
250 }
251
252 - (void)_windowDidBecomeKey:(NSNotification *)notification
253 {
254     NSWindow *keyWindow = [notification object];
255     if (keyWindow == [self window] || keyWindow == [[self window] attachedSheet])
256         [self _updateActiveState];
257 }
258
259 - (void)_windowDidResignKey:(NSNotification *)notification
260 {
261     NSWindow *formerKeyWindow = [notification object];
262     if (formerKeyWindow == [self window] || formerKeyWindow == [[self window] attachedSheet])
263         [self _updateActiveState];
264 }
265
266 - (void)drawRect:(NSRect)rect
267 {    
268     [[NSColor whiteColor] set];
269     NSRectFill(rect);
270
271     if (_data->_page->isValid() && _data->_page->drawingArea()) {
272         CGContextRef context = static_cast<CGContextRef>([[NSGraphicsContext currentContext] graphicsPort]);
273         _data->_page->drawingArea()->paint(IntRect(rect), context);
274     }
275 }
276
277 - (BOOL)isOpaque 
278 {
279     // FIXME: Return NO if we have a transparent background.
280     return YES;
281 }
282
283 - (void)viewDidHide
284 {
285     [self _updateVisibility];
286 }
287
288 - (void)viewDidUnhide
289 {
290     [self _updateVisibility];
291 }
292
293 @end
294
295 @implementation WKView (Internal)
296
297 - (void)_processDidExit
298 {
299     [self setNeedsDisplay:YES];
300 }
301
302 - (void)_processDidRevive
303 {
304     _data->_page->reinitializeWebPage(IntSize([self visibleRect].size));
305
306     _data->_page->setActive([[self window] isKeyWindow]);
307     _data->_page->setFocused([[self window] firstResponder] == self);
308     
309     [self setNeedsDisplay:YES];
310 }
311
312 - (void)_takeFocus:(BOOL)forward
313 {
314     if (forward)
315         [[self window] selectKeyViewFollowingView:self];
316     else
317         [[self window] selectKeyViewPrecedingView:self];
318 }
319
320
321 // Any non-zero value will do, but using something recognizable might help us debug some day.
322 #define TRACKING_RECT_TAG 0xBADFACE
323
324 - (NSTrackingRectTag)addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside
325 {
326     ASSERT(_data->_trackingRectOwner == nil);
327     _data->_trackingRectOwner = owner;
328     _data->_trackingRectUserData = data;
329     return TRACKING_RECT_TAG;
330 }
331
332 - (NSTrackingRectTag)_addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside useTrackingNum:(int)tag
333 {
334     ASSERT(tag == 0 || tag == TRACKING_RECT_TAG);
335     ASSERT(_data->_trackingRectOwner == nil);
336     _data->_trackingRectOwner = owner;
337     _data->_trackingRectUserData = data;
338     return TRACKING_RECT_TAG;
339 }
340
341 - (void)_addTrackingRects:(NSRect *)rects owner:(id)owner userDataList:(void **)userDataList assumeInsideList:(BOOL *)assumeInsideList trackingNums:(NSTrackingRectTag *)trackingNums count:(int)count
342 {
343     ASSERT(count == 1);
344     ASSERT(trackingNums[0] == 0 || trackingNums[0] == TRACKING_RECT_TAG);
345     ASSERT(_data->_trackingRectOwner == nil);
346     _data->_trackingRectOwner = owner;
347     _data->_trackingRectUserData = userDataList[0];
348     trackingNums[0] = TRACKING_RECT_TAG;
349 }
350
351 - (void)removeTrackingRect:(NSTrackingRectTag)tag
352 {
353     if (tag == 0)
354         return;
355     
356     if (_data && (tag == TRACKING_RECT_TAG)) {
357         _data->_trackingRectOwner = nil;
358         return;
359     }
360     
361     if (_data && (tag == _data->_lastToolTipTag)) {
362         [super removeTrackingRect:tag];
363         _data->_lastToolTipTag = 0;
364         return;
365     }
366     
367     // If any other tracking rect is being removed, we don't know how it was created
368     // and it's possible there's a leak involved (see 3500217)
369     ASSERT_NOT_REACHED();
370 }
371
372 - (void)_removeTrackingRects:(NSTrackingRectTag *)tags count:(int)count
373 {
374     int i;
375     for (i = 0; i < count; ++i) {
376         int tag = tags[i];
377         if (tag == 0)
378             continue;
379         ASSERT(tag == TRACKING_RECT_TAG);
380         if (_data != nil) {
381             _data->_trackingRectOwner = nil;
382         }
383     }
384 }
385
386 - (void)_sendToolTipMouseExited
387 {
388     // Nothing matters except window, trackingNumber, and userData.
389     NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseExited
390         location:NSMakePoint(0, 0)
391         modifierFlags:0
392         timestamp:0
393         windowNumber:[[self window] windowNumber]
394         context:NULL
395         eventNumber:0
396         trackingNumber:TRACKING_RECT_TAG
397         userData:_data->_trackingRectUserData];
398     [_data->_trackingRectOwner mouseExited:fakeEvent];
399 }
400
401 - (void)_sendToolTipMouseEntered
402 {
403     // Nothing matters except window, trackingNumber, and userData.
404     NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseEntered
405         location:NSMakePoint(0, 0)
406         modifierFlags:0
407         timestamp:0
408         windowNumber:[[self window] windowNumber]
409         context:NULL
410         eventNumber:0
411         trackingNumber:TRACKING_RECT_TAG
412         userData:_data->_trackingRectUserData];
413     [_data->_trackingRectOwner mouseEntered:fakeEvent];
414 }
415
416 - (NSString *)view:(NSView *)view stringForToolTip:(NSToolTipTag)tag point:(NSPoint)point userData:(void *)data
417 {
418     return nsStringFromWebCoreString(_data->_page->toolTip());
419 }
420
421 - (void)_toolTipChangedFrom:(NSString *)oldToolTip to:(NSString *)newToolTip
422 {
423     if (oldToolTip)
424         [self _sendToolTipMouseExited];
425
426     if (newToolTip && [newToolTip length] > 0) {
427         // See radar 3500217 for why we remove all tooltips rather than just the single one we created.
428         [self removeAllToolTips];
429         NSRect wideOpenRect = NSMakeRect(-100000, -100000, 200000, 200000);
430         _data->_lastToolTipTag = [self addToolTipRect:wideOpenRect owner:self userData:NULL];
431         [self _sendToolTipMouseEntered];
432     }
433 }
434
435 #if USE(ACCELERATED_COMPOSITING)
436 - (void)_startAcceleratedCompositing:(CALayer*)rootLayer
437 {
438     if (!_data->_layerHostingView) {
439         NSView* hostingView = [[NSView alloc] initWithFrame:[self bounds]];
440 #if !defined(BUILDING_ON_LEOPARD)
441         [hostingView setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)];
442 #endif
443         
444         [self addSubview:hostingView];
445         [hostingView release];
446         // hostingView is owned by being a subview of self
447         _data->_layerHostingView = hostingView;
448     }
449
450     // Make a container layer, which will get sized/positioned by AppKit and CA.
451     CALayer* viewLayer = [CALayer layer];
452
453 #if defined(BUILDING_ON_LEOPARD)
454     // Turn off default animations.
455     NSNull *nullValue = [NSNull null];
456     NSDictionary *actions = [NSDictionary dictionaryWithObjectsAndKeys:
457                              nullValue, @"anchorPoint",
458                              nullValue, @"bounds",
459                              nullValue, @"contents",
460                              nullValue, @"contentsRect",
461                              nullValue, @"opacity",
462                              nullValue, @"position",
463                              nullValue, @"sublayerTransform",
464                              nullValue, @"sublayers",
465                              nullValue, @"transform",
466                              nil];
467     [viewLayer setStyle:[NSDictionary dictionaryWithObject:actions forKey:@"actions"]];
468 #endif
469
470 #if !defined(BUILDING_ON_LEOPARD)
471     // If we aren't in the window yet, we'll use the screen's scale factor now, and reset the scale 
472     // via -viewDidMoveToWindow.
473     CGFloat scaleFactor = [self window] ? [[self window] userSpaceScaleFactor] : [[NSScreen mainScreen] userSpaceScaleFactor];
474     [viewLayer setTransform:CATransform3DMakeScale(scaleFactor, scaleFactor, 1)];
475 #endif
476
477     [_data->_layerHostingView setLayer:viewLayer];
478     [_data->_layerHostingView setWantsLayer:YES];
479     
480     // Parent our root layer in the container layer
481     [viewLayer addSublayer:rootLayer];
482 }
483
484 - (void)_stopAcceleratedCompositing
485 {
486     if (_data->_layerHostingView) {
487         [_data->_layerHostingView setLayer:nil];
488         [_data->_layerHostingView setWantsLayer:NO];
489         [_data->_layerHostingView removeFromSuperview];
490         _data->_layerHostingView = nil;
491     }
492 }
493 #endif // USE(ACCELERATED_COMPOSITING)
494
495 @end