|
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 _data->_page->setIsInWindow([self window]); |
|
91 |
|
92 return self; |
|
93 } |
|
94 |
|
95 - (id)initWithFrame:(NSRect)frame |
|
96 { |
|
97 WebContext* context = WebContext::sharedProcessContext(); |
|
98 self = [self initWithFrame:frame pageNamespaceRef:toRef(context->createPageNamespace())]; |
|
99 if (!self) |
|
100 return nil; |
|
101 |
|
102 return self; |
|
103 } |
|
104 |
|
105 - (void)dealloc |
|
106 { |
|
107 _data->_page->close(); |
|
108 |
|
109 [_data release]; |
|
110 [super dealloc]; |
|
111 } |
|
112 |
|
113 - (WKPageRef)pageRef |
|
114 { |
|
115 return toRef(_data->_page.get()); |
|
116 } |
|
117 |
|
118 - (BOOL)acceptsFirstResponder |
|
119 { |
|
120 return YES; |
|
121 } |
|
122 |
|
123 - (BOOL)becomeFirstResponder |
|
124 { |
|
125 _data->_page->setFocused(true); |
|
126 return YES; |
|
127 } |
|
128 |
|
129 - (BOOL)resignFirstResponder |
|
130 { |
|
131 _data->_page->setFocused(false); |
|
132 return YES; |
|
133 } |
|
134 |
|
135 - (BOOL)isFlipped |
|
136 { |
|
137 return YES; |
|
138 } |
|
139 |
|
140 - (void)setFrameSize:(NSSize)size |
|
141 { |
|
142 [super setFrameSize:size]; |
|
143 |
|
144 _data->_page->drawingArea()->setSize(IntSize(size)); |
|
145 } |
|
146 |
|
147 // Events |
|
148 |
|
149 - (void)mouseDown:(NSEvent *)theEvent |
|
150 { |
|
151 WebMouseEvent mouseEvent = WebEventFactory::createWebMouseEvent(theEvent, self); |
|
152 _data->_page->mouseEvent(mouseEvent); |
|
153 } |
|
154 |
|
155 - (void)mouseUp:(NSEvent *)theEvent |
|
156 { |
|
157 if (!_data->_page->isValid()) { |
|
158 _data->_page->revive(); |
|
159 _data->_page->loadURL(_data->_page->urlAtProcessExit()); |
|
160 return; |
|
161 } |
|
162 |
|
163 WebMouseEvent mouseEvent = WebEventFactory::createWebMouseEvent(theEvent, self); |
|
164 _data->_page->mouseEvent(mouseEvent); |
|
165 } |
|
166 |
|
167 - (void)mouseMoved:(NSEvent *)theEvent |
|
168 { |
|
169 WebMouseEvent mouseEvent = WebEventFactory::createWebMouseEvent(theEvent, self); |
|
170 _data->_page->mouseEvent(mouseEvent); |
|
171 } |
|
172 |
|
173 - (void)mouseDragged:(NSEvent *)theEvent |
|
174 { |
|
175 WebMouseEvent mouseEvent = WebEventFactory::createWebMouseEvent(theEvent, self); |
|
176 _data->_page->mouseEvent(mouseEvent); |
|
177 } |
|
178 |
|
179 - (void)scrollWheel:(NSEvent *)theEvent |
|
180 { |
|
181 WebWheelEvent wheelEvent = WebEventFactory::createWebWheelEvent(theEvent, self); |
|
182 _data->_page->wheelEvent(wheelEvent); |
|
183 } |
|
184 |
|
185 - (void)keyUp:(NSEvent *)theEvent |
|
186 { |
|
187 WebKeyboardEvent keyboardEvent = WebEventFactory::createWebKeyboardEvent(theEvent); |
|
188 _data->_page->keyEvent(keyboardEvent); |
|
189 } |
|
190 |
|
191 - (void)keyDown:(NSEvent *)theEvent |
|
192 { |
|
193 WebKeyboardEvent keyboardEvent = WebEventFactory::createWebKeyboardEvent(theEvent); |
|
194 _data->_page->keyEvent(keyboardEvent); |
|
195 } |
|
196 |
|
197 - (void)_updateActiveState |
|
198 { |
|
199 _data->_page->setActive([[self window] isKeyWindow]); |
|
200 } |
|
201 |
|
202 - (void)addWindowObserversForWindow:(NSWindow *)window |
|
203 { |
|
204 if (window) { |
|
205 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowDidBecomeKey:) |
|
206 name:NSWindowDidBecomeKeyNotification object:nil]; |
|
207 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowDidResignKey:) |
|
208 name:NSWindowDidResignKeyNotification object:nil]; |
|
209 } |
|
210 } |
|
211 |
|
212 - (void)removeWindowObservers |
|
213 { |
|
214 NSWindow *window = [self window]; |
|
215 if (window) { |
|
216 [[NSNotificationCenter defaultCenter] removeObserver:self |
|
217 name:NSWindowDidBecomeKeyNotification object:nil]; |
|
218 [[NSNotificationCenter defaultCenter] removeObserver:self |
|
219 name:NSWindowDidResignKeyNotification object:nil]; |
|
220 } |
|
221 } |
|
222 |
|
223 static bool isViewVisible(NSView *view) |
|
224 { |
|
225 if (![view window]) |
|
226 return false; |
|
227 |
|
228 if ([view isHiddenOrHasHiddenAncestor]) |
|
229 return false; |
|
230 |
|
231 return true; |
|
232 } |
|
233 |
|
234 - (void)_updateVisibility |
|
235 { |
|
236 _data->_page->setIsInWindow([self window]); |
|
237 _data->_page->drawingArea()->setPageIsVisible(isViewVisible(self)); |
|
238 } |
|
239 |
|
240 - (void)viewWillMoveToWindow:(NSWindow *)window |
|
241 { |
|
242 if (window != [self window]) { |
|
243 [self removeWindowObservers]; |
|
244 [self addWindowObserversForWindow:window]; |
|
245 } |
|
246 } |
|
247 |
|
248 - (void)viewDidMoveToWindow |
|
249 { |
|
250 // We want to make sure to update the active state while hidden, so if the view is about to become visible, we |
|
251 // update the active state first and then make it visible. If the view is about to be hidden, we hide it first and then |
|
252 // update the active state. |
|
253 if ([self window]) { |
|
254 [self _updateActiveState]; |
|
255 [self _updateVisibility]; |
|
256 } else { |
|
257 [self _updateVisibility]; |
|
258 [self _updateActiveState]; |
|
259 } |
|
260 } |
|
261 |
|
262 - (void)_windowDidBecomeKey:(NSNotification *)notification |
|
263 { |
|
264 NSWindow *keyWindow = [notification object]; |
|
265 if (keyWindow == [self window] || keyWindow == [[self window] attachedSheet]) |
|
266 [self _updateActiveState]; |
|
267 } |
|
268 |
|
269 - (void)_windowDidResignKey:(NSNotification *)notification |
|
270 { |
|
271 NSWindow *formerKeyWindow = [notification object]; |
|
272 if (formerKeyWindow == [self window] || formerKeyWindow == [[self window] attachedSheet]) |
|
273 [self _updateActiveState]; |
|
274 } |
|
275 |
|
276 - (void)drawRect:(NSRect)rect |
|
277 { |
|
278 [[NSColor whiteColor] set]; |
|
279 NSRectFill(rect); |
|
280 |
|
281 if (_data->_page->isValid() && _data->_page->drawingArea()) { |
|
282 CGContextRef context = static_cast<CGContextRef>([[NSGraphicsContext currentContext] graphicsPort]); |
|
283 _data->_page->drawingArea()->paint(IntRect(rect), context); |
|
284 } |
|
285 } |
|
286 |
|
287 - (BOOL)isOpaque |
|
288 { |
|
289 // FIXME: Return NO if we have a transparent background. |
|
290 return YES; |
|
291 } |
|
292 |
|
293 - (void)viewDidHide |
|
294 { |
|
295 [self _updateVisibility]; |
|
296 } |
|
297 |
|
298 - (void)viewDidUnhide |
|
299 { |
|
300 [self _updateVisibility]; |
|
301 } |
|
302 |
|
303 @end |
|
304 |
|
305 @implementation WKView (Internal) |
|
306 |
|
307 - (void)_processDidExit |
|
308 { |
|
309 [self setNeedsDisplay:YES]; |
|
310 } |
|
311 |
|
312 - (void)_processDidRevive |
|
313 { |
|
314 _data->_page->reinitializeWebPage(IntSize([self visibleRect].size)); |
|
315 |
|
316 _data->_page->setActive([[self window] isKeyWindow]); |
|
317 _data->_page->setFocused([[self window] firstResponder] == self); |
|
318 |
|
319 [self setNeedsDisplay:YES]; |
|
320 } |
|
321 |
|
322 - (void)_takeFocus:(BOOL)forward |
|
323 { |
|
324 if (forward) |
|
325 [[self window] selectKeyViewFollowingView:self]; |
|
326 else |
|
327 [[self window] selectKeyViewPrecedingView:self]; |
|
328 } |
|
329 |
|
330 - (void)_setCursor:(NSCursor *)cursor |
|
331 { |
|
332 if ([NSCursor currentCursor] == cursor) |
|
333 return; |
|
334 [cursor set]; |
|
335 } |
|
336 |
|
337 // Any non-zero value will do, but using something recognizable might help us debug some day. |
|
338 #define TRACKING_RECT_TAG 0xBADFACE |
|
339 |
|
340 - (NSTrackingRectTag)addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside |
|
341 { |
|
342 ASSERT(_data->_trackingRectOwner == nil); |
|
343 _data->_trackingRectOwner = owner; |
|
344 _data->_trackingRectUserData = data; |
|
345 return TRACKING_RECT_TAG; |
|
346 } |
|
347 |
|
348 - (NSTrackingRectTag)_addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside useTrackingNum:(int)tag |
|
349 { |
|
350 ASSERT(tag == 0 || tag == TRACKING_RECT_TAG); |
|
351 ASSERT(_data->_trackingRectOwner == nil); |
|
352 _data->_trackingRectOwner = owner; |
|
353 _data->_trackingRectUserData = data; |
|
354 return TRACKING_RECT_TAG; |
|
355 } |
|
356 |
|
357 - (void)_addTrackingRects:(NSRect *)rects owner:(id)owner userDataList:(void **)userDataList assumeInsideList:(BOOL *)assumeInsideList trackingNums:(NSTrackingRectTag *)trackingNums count:(int)count |
|
358 { |
|
359 ASSERT(count == 1); |
|
360 ASSERT(trackingNums[0] == 0 || trackingNums[0] == TRACKING_RECT_TAG); |
|
361 ASSERT(_data->_trackingRectOwner == nil); |
|
362 _data->_trackingRectOwner = owner; |
|
363 _data->_trackingRectUserData = userDataList[0]; |
|
364 trackingNums[0] = TRACKING_RECT_TAG; |
|
365 } |
|
366 |
|
367 - (void)removeTrackingRect:(NSTrackingRectTag)tag |
|
368 { |
|
369 if (tag == 0) |
|
370 return; |
|
371 |
|
372 if (_data && (tag == TRACKING_RECT_TAG)) { |
|
373 _data->_trackingRectOwner = nil; |
|
374 return; |
|
375 } |
|
376 |
|
377 if (_data && (tag == _data->_lastToolTipTag)) { |
|
378 [super removeTrackingRect:tag]; |
|
379 _data->_lastToolTipTag = 0; |
|
380 return; |
|
381 } |
|
382 |
|
383 // If any other tracking rect is being removed, we don't know how it was created |
|
384 // and it's possible there's a leak involved (see 3500217) |
|
385 ASSERT_NOT_REACHED(); |
|
386 } |
|
387 |
|
388 - (void)_removeTrackingRects:(NSTrackingRectTag *)tags count:(int)count |
|
389 { |
|
390 int i; |
|
391 for (i = 0; i < count; ++i) { |
|
392 int tag = tags[i]; |
|
393 if (tag == 0) |
|
394 continue; |
|
395 ASSERT(tag == TRACKING_RECT_TAG); |
|
396 if (_data != nil) { |
|
397 _data->_trackingRectOwner = nil; |
|
398 } |
|
399 } |
|
400 } |
|
401 |
|
402 - (void)_sendToolTipMouseExited |
|
403 { |
|
404 // Nothing matters except window, trackingNumber, and userData. |
|
405 NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseExited |
|
406 location:NSMakePoint(0, 0) |
|
407 modifierFlags:0 |
|
408 timestamp:0 |
|
409 windowNumber:[[self window] windowNumber] |
|
410 context:NULL |
|
411 eventNumber:0 |
|
412 trackingNumber:TRACKING_RECT_TAG |
|
413 userData:_data->_trackingRectUserData]; |
|
414 [_data->_trackingRectOwner mouseExited:fakeEvent]; |
|
415 } |
|
416 |
|
417 - (void)_sendToolTipMouseEntered |
|
418 { |
|
419 // Nothing matters except window, trackingNumber, and userData. |
|
420 NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseEntered |
|
421 location:NSMakePoint(0, 0) |
|
422 modifierFlags:0 |
|
423 timestamp:0 |
|
424 windowNumber:[[self window] windowNumber] |
|
425 context:NULL |
|
426 eventNumber:0 |
|
427 trackingNumber:TRACKING_RECT_TAG |
|
428 userData:_data->_trackingRectUserData]; |
|
429 [_data->_trackingRectOwner mouseEntered:fakeEvent]; |
|
430 } |
|
431 |
|
432 - (NSString *)view:(NSView *)view stringForToolTip:(NSToolTipTag)tag point:(NSPoint)point userData:(void *)data |
|
433 { |
|
434 return nsStringFromWebCoreString(_data->_page->toolTip()); |
|
435 } |
|
436 |
|
437 - (void)_toolTipChangedFrom:(NSString *)oldToolTip to:(NSString *)newToolTip |
|
438 { |
|
439 if (oldToolTip) |
|
440 [self _sendToolTipMouseExited]; |
|
441 |
|
442 if (newToolTip && [newToolTip length] > 0) { |
|
443 // See radar 3500217 for why we remove all tooltips rather than just the single one we created. |
|
444 [self removeAllToolTips]; |
|
445 NSRect wideOpenRect = NSMakeRect(-100000, -100000, 200000, 200000); |
|
446 _data->_lastToolTipTag = [self addToolTipRect:wideOpenRect owner:self userData:NULL]; |
|
447 [self _sendToolTipMouseEntered]; |
|
448 } |
|
449 } |
|
450 |
|
451 #if USE(ACCELERATED_COMPOSITING) |
|
452 - (void)_startAcceleratedCompositing:(CALayer *)rootLayer |
|
453 { |
|
454 if (!_data->_layerHostingView) { |
|
455 NSView *hostingView = [[NSView alloc] initWithFrame:[self bounds]]; |
|
456 #if !defined(BUILDING_ON_LEOPARD) |
|
457 [hostingView setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)]; |
|
458 #endif |
|
459 |
|
460 [self addSubview:hostingView]; |
|
461 [hostingView release]; |
|
462 _data->_layerHostingView = hostingView; |
|
463 } |
|
464 |
|
465 // Make a container layer, which will get sized/positioned by AppKit and CA. |
|
466 CALayer *viewLayer = [CALayer layer]; |
|
467 |
|
468 #ifndef NDEBUG |
|
469 [viewLayer setName:@"hosting layer"]; |
|
470 #endif |
|
471 |
|
472 #if defined(BUILDING_ON_LEOPARD) |
|
473 // Turn off default animations. |
|
474 NSNull *nullValue = [NSNull null]; |
|
475 NSDictionary *actions = [NSDictionary dictionaryWithObjectsAndKeys: |
|
476 nullValue, @"anchorPoint", |
|
477 nullValue, @"bounds", |
|
478 nullValue, @"contents", |
|
479 nullValue, @"contentsRect", |
|
480 nullValue, @"opacity", |
|
481 nullValue, @"position", |
|
482 nullValue, @"sublayerTransform", |
|
483 nullValue, @"sublayers", |
|
484 nullValue, @"transform", |
|
485 nil]; |
|
486 [viewLayer setStyle:[NSDictionary dictionaryWithObject:actions forKey:@"actions"]]; |
|
487 #endif |
|
488 |
|
489 #if !defined(BUILDING_ON_LEOPARD) |
|
490 // If we aren't in the window yet, we'll use the screen's scale factor now, and reset the scale |
|
491 // via -viewDidMoveToWindow. |
|
492 CGFloat scaleFactor = [self window] ? [[self window] userSpaceScaleFactor] : [[NSScreen mainScreen] userSpaceScaleFactor]; |
|
493 [viewLayer setTransform:CATransform3DMakeScale(scaleFactor, scaleFactor, 1)]; |
|
494 #endif |
|
495 |
|
496 [_data->_layerHostingView setLayer:viewLayer]; |
|
497 [_data->_layerHostingView setWantsLayer:YES]; |
|
498 |
|
499 // Parent our root layer in the container layer |
|
500 [viewLayer addSublayer:rootLayer]; |
|
501 } |
|
502 |
|
503 - (void)_stopAcceleratedCompositing |
|
504 { |
|
505 if (_data->_layerHostingView) { |
|
506 [_data->_layerHostingView setLayer:nil]; |
|
507 [_data->_layerHostingView setWantsLayer:NO]; |
|
508 [_data->_layerHostingView removeFromSuperview]; |
|
509 _data->_layerHostingView = nil; |
|
510 } |
|
511 } |
|
512 |
|
513 - (void)_switchToDrawingAreaTypeIfNecessary:(DrawingAreaProxy::Type)type |
|
514 { |
|
515 DrawingAreaProxy::Type existingDrawingAreaType = _data->_page->drawingArea() ? _data->_page->drawingArea()->type() : DrawingAreaProxy::None; |
|
516 if (existingDrawingAreaType == type) |
|
517 return; |
|
518 |
|
519 OwnPtr<DrawingAreaProxy> newDrawingArea; |
|
520 switch (type) { |
|
521 case DrawingAreaProxy::None: |
|
522 break; |
|
523 case DrawingAreaProxy::ChunkedUpdateDrawingAreaType: { |
|
524 newDrawingArea = new ChunkedUpdateDrawingAreaProxy(self); |
|
525 break; |
|
526 } |
|
527 case DrawingAreaProxy::LayerBackedDrawingAreaType: { |
|
528 newDrawingArea = new LayerBackedDrawingAreaProxy(self); |
|
529 break; |
|
530 } |
|
531 } |
|
532 |
|
533 newDrawingArea->setSize(IntSize([self frame].size)); |
|
534 |
|
535 _data->_page->drawingArea()->detachCompositingContext(); |
|
536 _data->_page->setDrawingArea(newDrawingArea.release()); |
|
537 } |
|
538 |
|
539 - (void)_pageDidEnterAcceleratedCompositing |
|
540 { |
|
541 [self _switchToDrawingAreaTypeIfNecessary:DrawingAreaProxy::LayerBackedDrawingAreaType]; |
|
542 } |
|
543 |
|
544 - (void)_pageDidLeaveAcceleratedCompositing |
|
545 { |
|
546 // FIXME: we may want to avoid flipping back to the non-layer-backed drawing area until the next page load, to avoid thrashing. |
|
547 [self _switchToDrawingAreaTypeIfNecessary:DrawingAreaProxy::ChunkedUpdateDrawingAreaType]; |
|
548 } |
|
549 #endif // USE(ACCELERATED_COMPOSITING) |
|
550 |
|
551 @end |