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