1/*
2 * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2014 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 "config.h"
27#import "WAKWindow.h"
28
29#if PLATFORM(IOS)
30
31#import "LegacyTileCache.h"
32#import "WAKViewPrivate.h"
33#import "WebCoreSystemInterface.h"
34#import "WebCoreThreadRun.h"
35#import "WebEvent.h"
36#import "WKContentObservation.h"
37#import "WKViewPrivate.h"
38#import <QuartzCore/QuartzCore.h>
39#import <wtf/TCSpinLock.h>
40
41NSString * const WAKWindowScreenScaleDidChangeNotification = @"WAKWindowScreenScaleDidChangeNotification";
42NSString * const WAKWindowVisibilityDidChangeNotification = @"WAKWindowVisibilityDidChangeNotification";
43
44using namespace WebCore;
45
46@protocol OrientationProvider
47#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 60000
48- (BOOL)hasLandscapeOrientation;
49#else
50- (int)orientation;
51#endif
52@end
53
54static WAKWindow *_WAKKeyWindow = nil;        // weak
55static WebEvent *currentEvent = nil;
56static id<OrientationProvider> gOrientationProvider;
57
58@implementation WAKWindow {
59    SpinLock _exposedScrollViewRectLock;
60    CGRect _exposedScrollViewRect;
61}
62
63@synthesize useOrientationDependentFontAntialiasing = _useOrientationDependentFontAntialiasing;
64
65- (id)initWithLayer:(CALayer *)layer
66{
67    self = [super init];
68    if (!self)
69        return nil;
70
71    _hostLayer = [layer retain];
72
73    _frame = [_hostLayer frame];
74    _screenScale = wkGetScreenScaleFactor();
75
76    _tileCache = new LegacyTileCache(self);
77
78    _frozenVisibleRect = CGRectNull;
79
80    _exposedScrollViewRectLock = SPINLOCK_INITIALIZER;
81    _exposedScrollViewRect = CGRectNull;
82
83    return self;
84}
85
86// This is used for WebViews that are not backed by the tile cache. Their content must be painted manually.
87- (id)initWithFrame:(CGRect)frame
88{
89    self = [super init];
90    if (!self)
91        return nil;
92
93    _frame = frame;
94    _screenScale = wkGetScreenScaleFactor();
95
96    _exposedScrollViewRectLock = SPINLOCK_INITIALIZER;
97    _exposedScrollViewRect = CGRectNull;
98
99    return self;
100}
101
102- (void)dealloc
103{
104    delete _tileCache;
105    [_hostLayer release];
106
107    [super dealloc];
108}
109
110- (void)setContentView:(WAKView *)aView
111{
112    [aView retain];
113    [_contentView release];
114
115    if (aView)
116        _WKViewSetWindow([aView _viewRef], self);
117    _contentView = aView;
118}
119
120- (WAKView *)contentView
121{
122    return _contentView;
123}
124
125- (void)close
126{
127    if (_contentView) {
128        _WKViewSetWindow([_contentView _viewRef], nil);
129        [_contentView release];
130        _contentView = nil;
131    }
132
133    [_responderView release];
134    _responderView = nil;
135}
136
137- (WAKView *)firstResponder
138{
139    return _responderView;
140}
141
142- (WAKView *)_newFirstResponderAfterResigning
143{
144    return _nextResponder;
145}
146
147- (NSPoint)convertBaseToScreen:(NSPoint)aPoint
148{
149    CALayer* rootLayer = _hostLayer;
150    while (rootLayer.superlayer)
151        rootLayer = rootLayer.superlayer;
152
153    return [_hostLayer convertPoint:aPoint toLayer:rootLayer];
154}
155
156- (NSPoint)convertScreenToBase:(NSPoint)aPoint
157{
158    CALayer* rootLayer = _hostLayer;
159    while (rootLayer.superlayer)
160        rootLayer = rootLayer.superlayer;
161
162    return [_hostLayer convertPoint:aPoint fromLayer:rootLayer];
163}
164
165- (BOOL)isKeyWindow
166{
167    return YES || self == _WAKKeyWindow;
168}
169
170- (void)makeKeyWindow
171{
172    if ([self isKeyWindow])
173        return;
174
175    _WAKKeyWindow = self;
176}
177
178- (BOOL)isVisible
179{
180    return _visible;
181}
182
183- (void)setVisible:(BOOL)visible
184{
185    if (_visible == visible)
186        return;
187
188    _visible = visible;
189
190    WebThreadRun(^{
191        [[NSNotificationCenter defaultCenter] postNotificationName:WAKWindowVisibilityDidChangeNotification object:self userInfo:nil];
192    });
193}
194
195- (NSSelectionDirection)keyViewSelectionDirection
196{
197    return (NSSelectionDirection)0;
198}
199
200- (BOOL)makeFirstResponder:(NSResponder *)aResponder
201{
202    if (![aResponder isKindOfClass:[WAKView class]])
203        return NO;
204
205    WAKView *view = static_cast<WAKView*>(aResponder);
206    BOOL result = YES;
207    if (view != _responderView) {
208        // We need to handle the case of the view not accepting to be a first responder,
209        // where we don't need to resign as first responder.
210        BOOL acceptsFirstResponder = view ? WKViewAcceptsFirstResponder([view _viewRef]) : YES;
211        if (acceptsFirstResponder && _responderView) {
212            _nextResponder = view;
213            if (WKViewResignFirstResponder([_responderView _viewRef])) {
214                _nextResponder = nil;
215                [_responderView release];
216                _responderView = nil;
217            }  else {
218                _nextResponder = nil;
219                result = NO;
220            }
221        }
222
223        if (result && view) {
224            if (acceptsFirstResponder && WKViewBecomeFirstResponder([view _viewRef]))
225                _responderView = [view retain];
226            else
227                result = NO;
228        }
229    }
230
231    return result;
232}
233
234// FIXME: This method can lead to incorrect state. Remove if possible.
235- (void)setFrame:(NSRect)frameRect display:(BOOL)flag
236{
237    UNUSED_PARAM(flag);
238    _frame = frameRect;
239}
240
241// FIXME: the correct value if there is a host layer is likely to return [_hostLayer frame].
242- (CGRect)frame
243{
244    return _frame;
245}
246
247- (void)setContentRect:(CGRect)rect
248{
249    if (CGRectEqualToRect(rect, _frame))
250        return;
251    _frame = rect;
252
253    // FIXME: Size of the host layer is directly available so there is no real reason to save it here.
254    // However we currently use this call to catch changes to the host layer size.
255    if (_tileCache)
256        _tileCache->hostLayerSizeChanged();
257}
258
259- (void)setScreenSize:(CGSize)size
260{
261    _screenSize = size;
262}
263
264- (CGSize)screenSize
265{
266    return _screenSize;
267}
268
269- (void)setAvailableScreenSize:(CGSize)size
270{
271    _availableScreenSize = size;
272}
273
274- (CGSize)availableScreenSize
275{
276    return _availableScreenSize;
277}
278
279- (void)setScreenScale:(CGFloat)scale
280{
281    _screenScale = scale;
282
283    WebThreadRun(^{
284        [[NSNotificationCenter defaultCenter] postNotificationName:WAKWindowScreenScaleDidChangeNotification object:self userInfo:nil];
285    });
286}
287
288- (CGFloat)screenScale
289{
290    return _screenScale;
291}
292
293- (void)setRootLayer:(CALayer *)layer
294{
295    _rootLayer = layer;
296}
297
298- (CALayer *)rootLayer
299{
300    return _rootLayer;
301}
302
303- (void)sendEvent:(WebEvent *)anEvent
304{
305    ASSERT(anEvent);
306    WebThreadRun(^{
307        [self sendEventSynchronously:anEvent];
308    });
309}
310
311- (void)sendEventSynchronously:(WebEvent *)anEvent
312{
313    ASSERT(anEvent);
314    ASSERT(WebThreadIsLockedOrDisabled());
315    WebEvent *lastEvent = currentEvent;
316    currentEvent = [anEvent retain];
317
318    switch (anEvent.type) {
319        case WebEventMouseMoved:
320        case WebEventScrollWheel:
321            if (WAKView *hitView = [_contentView hitTest:(anEvent.locationInWindow)])
322                [hitView handleEvent:anEvent];
323            break;
324
325        case WebEventMouseUp:
326        case WebEventKeyDown:
327        case WebEventKeyUp:
328        case WebEventTouchChange:
329            [_responderView handleEvent:anEvent];
330            break;
331
332        case WebEventMouseDown:
333        case WebEventTouchBegin:
334        case WebEventTouchEnd:
335        case WebEventTouchCancel:
336            if (WAKView *hitView = [_contentView hitTest:(anEvent.locationInWindow)]) {
337                [self makeFirstResponder:hitView];
338                [hitView handleEvent:anEvent];
339            }
340            break;
341
342        default:
343            ASSERT_NOT_REACHED();
344            break;
345    }
346
347    [currentEvent release];
348    currentEvent = lastEvent;
349}
350
351- (void)sendMouseMoveEvent:(WebEvent *)anEvent contentChange:(WKContentChange *)aContentChange
352{
353    WebThreadRun(^{
354        [self sendEvent:anEvent];
355
356        if (aContentChange)
357            *aContentChange = WKObservedContentChange();
358    });
359}
360
361- (void)setExposedScrollViewRect:(CGRect)exposedScrollViewRect
362{
363    SpinLockHolder locker(&_exposedScrollViewRectLock);
364    _exposedScrollViewRect = exposedScrollViewRect;
365}
366
367- (CGRect)exposedScrollViewRect
368{
369    {
370        SpinLockHolder locker(&_exposedScrollViewRectLock);
371        if (!CGRectIsNull(_exposedScrollViewRect))
372            return _exposedScrollViewRect;
373    }
374    return [self visibleRect];
375}
376
377// Tiling
378
379- (void)layoutTiles
380{
381    if (!_tileCache)
382        return;
383    _tileCache->layoutTiles();
384}
385
386- (void)layoutTilesNow
387{
388    if (!_tileCache)
389        return;
390    _tileCache->layoutTilesNow();
391}
392
393- (void)layoutTilesNowForRect:(CGRect)rect
394{
395    if (!_tileCache)
396        return;
397    _tileCache->layoutTilesNowForRect(enclosingIntRect(rect));
398}
399
400- (void)setNeedsDisplay
401{
402    if (!_tileCache)
403        return;
404    _tileCache->setNeedsDisplay();
405}
406
407- (void)setNeedsDisplayInRect:(CGRect)rect
408{
409    if (!_tileCache)
410        return;
411    _tileCache->setNeedsDisplayInRect(enclosingIntRect(rect));
412}
413
414- (BOOL)tilesOpaque
415{
416    if (!_tileCache)
417        return NO;
418    return _tileCache->tilesOpaque();
419}
420
421- (void)setTilesOpaque:(BOOL)opaque
422{
423    if (!_tileCache)
424        return;
425    _tileCache->setTilesOpaque(opaque);
426}
427
428- (CGRect)_visibleRectRespectingMasksToBounds:(BOOL)respectsMasksToBounds
429{
430    if (!CGRectIsNull(_frozenVisibleRect))
431        return _frozenVisibleRect;
432
433    CALayer* layer = _hostLayer;
434    CGRect bounds = [layer bounds];
435    CGRect rect = bounds;
436    CALayer* superlayer = [layer superlayer];
437
438    while (superlayer && layer != _rootLayer) {
439        CGRect rectInSuper = [superlayer convertRect:rect fromLayer:layer];
440        if ([superlayer masksToBounds] || !respectsMasksToBounds)
441            rect = CGRectIntersection([superlayer bounds], rectInSuper);
442        else
443            rect = rectInSuper;
444        layer = superlayer;
445        superlayer = [layer superlayer];
446    }
447
448    if (layer != _hostLayer) {
449        rect = CGRectIntersection([layer bounds], rect);
450        rect = [_hostLayer convertRect:rect fromLayer:layer];
451    }
452
453    return CGRectIntersection(bounds, rect);
454}
455
456- (CGRect)visibleRect
457{
458    return [self _visibleRectRespectingMasksToBounds:NO];
459}
460
461- (CGRect)extendedVisibleRect
462{
463    return [self _visibleRectRespectingMasksToBounds:YES];
464}
465
466- (void)removeAllNonVisibleTiles
467{
468    if (!_tileCache)
469        return;
470    _tileCache->removeAllNonVisibleTiles();
471}
472
473- (void)removeAllTiles
474{
475    if (!_tileCache)
476        return;
477    _tileCache->removeAllTiles();
478}
479
480- (void)removeForegroundTiles
481{
482    if (!_tileCache)
483        return;
484    _tileCache->removeForegroundTiles();
485}
486
487- (void)setTilingMode:(WAKWindowTilingMode)mode
488{
489    if (!_tileCache)
490        return;
491    _tileCache->setTilingMode((LegacyTileCache::TilingMode)mode);
492}
493
494- (WAKWindowTilingMode)tilingMode
495{
496    if (!_tileCache)
497        return kWAKWindowTilingModeDisabled;
498    return (WAKWindowTilingMode)_tileCache->tilingMode();
499}
500
501- (void)setTilingDirection:(WAKTilingDirection)tilingDirection
502{
503    if (!_tileCache)
504        return;
505    _tileCache->setTilingDirection((LegacyTileCache::TilingDirection)tilingDirection);
506}
507
508- (WAKTilingDirection)tilingDirection
509{
510    if (!_tileCache)
511        return kWAKTilingDirectionDown;
512    return (WAKTilingDirection)_tileCache->tilingDirection();
513}
514
515- (void)setZoomedOutTileScale:(float)scale
516{
517    if (!_tileCache)
518        return;
519    _tileCache->setZoomedOutScale(scale);
520}
521
522- (float)zoomedOutTileScale
523{
524    if (!_tileCache)
525        return 1.0f;
526    return _tileCache->zoomedOutScale();
527}
528
529- (void)setCurrentTileScale:(float)scale
530{
531    if (!_tileCache)
532        return;
533    _tileCache->setCurrentScale(scale);
534}
535
536- (float)currentTileScale
537{
538    if (!_tileCache)
539        return 1.0f;
540    return _tileCache->currentScale();
541}
542
543- (void)setKeepsZoomedOutTiles:(BOOL)keepsZoomedOutTiles
544{
545    if (!_tileCache)
546        return;
547    _tileCache->setKeepsZoomedOutTiles(keepsZoomedOutTiles);
548}
549
550- (BOOL)keepsZoomedOutTiles
551{
552    if (!_tileCache)
553        return NO;
554    return _tileCache->keepsZoomedOutTiles();
555}
556
557- (LegacyTileCache*)tileCache
558{
559    return _tileCache;
560}
561
562- (BOOL)hasPendingDraw
563{
564    if (!_tileCache)
565        return NO;
566     return _tileCache->hasPendingDraw();
567}
568
569- (void)setContentReplacementImage:(CGImageRef)contentReplacementImage
570{
571    if (!_tileCache)
572        return;
573    _tileCache->setContentReplacementImage(contentReplacementImage);
574}
575
576- (CGImageRef)contentReplacementImage
577{
578    if (!_tileCache)
579        return NULL;
580    return _tileCache->contentReplacementImage().get();
581}
582
583- (void)displayRect:(NSRect)rect
584{
585    [[self contentView] displayRect:rect];
586}
587
588- (void)willRotate
589{
590    [self freezeVisibleRect];
591}
592
593- (void)didRotate
594{
595    [self unfreezeVisibleRect];
596}
597
598- (void)freezeVisibleRect
599{
600    _frozenVisibleRect = [self visibleRect];
601}
602
603- (void)unfreezeVisibleRect
604{
605    _frozenVisibleRect = CGRectNull;
606}
607
608+ (void)setOrientationProvider:(id)provider
609{
610    // This is really the UIWebDocumentView class that calls into UIApplication to get the orientation.
611    gOrientationProvider = provider;
612}
613
614+ (BOOL)hasLandscapeOrientation
615{
616    // this should be perfectly thread safe
617#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 60000
618    return [gOrientationProvider hasLandscapeOrientation];
619#else
620    int orientation = [gOrientationProvider orientation];
621    return orientation == 90 || orientation == -90;
622#endif
623}
624
625- (CALayer*)hostLayer
626{
627    return _hostLayer;
628}
629
630- (void)setTileBordersVisible:(BOOL)visible
631{
632    if (!_tileCache)
633        return;
634
635    _tileCache->setTileBordersVisible(visible);
636}
637
638- (void)setTilePaintCountsVisible:(BOOL)visible
639{
640    if (!_tileCache)
641        return;
642
643    _tileCache->setTilePaintCountersVisible(visible);
644}
645
646- (void)setAcceleratedDrawingEnabled:(BOOL)enabled
647{
648    if (!_tileCache)
649        return;
650
651    _tileCache->setAcceleratedDrawingEnabled(enabled);
652}
653
654- (void)dumpTiles
655{
656    CGRect savedFrozenVisibleRect = _frozenVisibleRect;
657    NSLog(@"=================");
658    if (!CGRectIsNull(_frozenVisibleRect)) {
659        NSLog(@"VISIBLE RECT IS CACHED: [%6.1f %6.1f %6.1f %6.1f]", _frozenVisibleRect.origin.x, _frozenVisibleRect.origin.y, _frozenVisibleRect.size.width, _frozenVisibleRect.size.height);
660        _frozenVisibleRect = CGRectNull;
661    }
662    CGRect visibleRect = [self visibleRect];
663    NSLog(@"visibleRect = [%6.1f %6.1f %6.1f %6.1f]", visibleRect.origin.x, visibleRect.origin.y, visibleRect.size.width, visibleRect.size.height);
664    _frozenVisibleRect = savedFrozenVisibleRect;
665    _tileCache->dumpTiles();
666}
667
668- (void)setTileControllerShouldUseLowScaleTiles:(BOOL)lowScaleTiles
669{
670    if (!_tileCache)
671        return;
672
673    _tileCache->setTileControllerShouldUseLowScaleTiles(lowScaleTiles);
674}
675
676- (NSString *)description
677{
678    NSMutableString *description = [NSMutableString stringWithFormat:@"<%@: WAK: %p; ", [self class], self];
679
680    CGRect frame = [self frame];
681    [description appendFormat:@"frame = (%g %g; %g %g); ", frame.origin.x, frame.origin.y, frame.size.width, frame.size.height];
682
683    [description appendFormat:@"first responder = WK %p; ", _responderView];
684    [description appendFormat:@"next responder = WK %p; ", _nextResponder];
685
686    [description appendFormat:@"layer = %@>", [_hostLayer description]];
687
688    return description;
689}
690
691+ (WebEvent *)currentEvent
692{
693    return currentEvent;
694}
695
696- (NSString *)recursiveDescription
697{
698    NSMutableString *info = [NSMutableString string];
699
700    [info appendString:[self description]];
701
702    [[self contentView] _appendDescriptionToString:info atLevel:1];
703
704    return info;
705}
706
707@end
708
709#endif // PLATFORM(IOS)
710