blob: 1367dfb634f77284d02df58e0001ec528704282c [file] [log] [blame]
// Copyright 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/chrome/browser/snapshots/snapshot_manager.h"
#import <QuartzCore/QuartzCore.h>
#import <WebKit/WebKit.h>
#include "base/logging.h"
#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
#import "ios/chrome/browser/snapshots/snapshot_cache.h"
#import "ios/chrome/browser/snapshots/snapshot_cache_factory.h"
#import "ios/chrome/browser/snapshots/snapshot_overlay.h"
#import "ios/web/public/web_state/web_state.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace {
// Returns YES if |view| or any view it contains is a WKWebView.
BOOL ViewHierarchyContainsWKWebView(UIView* view) {
if ([view isKindOfClass:[WKWebView class]])
return YES;
for (UIView* subview in view.subviews) {
if (ViewHierarchyContainsWKWebView(subview))
return YES;
}
return NO;
}
} // namespace
@implementation SnapshotManager {
web::WebState* _webState;
}
- (instancetype)initWithWebState:(web::WebState*)webState {
if ((self = [super init])) {
DCHECK(webState);
_webState = webState;
}
return self;
}
- (UIImage*)generateSnapshotForView:(UIView*)view
withRect:(CGRect)rect
overlays:(NSArray*)overlays {
DCHECK(view);
CGSize size = rect.size;
DCHECK(std::isnormal(size.width) && (size.width > 0))
<< ": size.width=" << size.width;
DCHECK(std::isnormal(size.height) && (size.height > 0))
<< ": size.height=" << size.height;
const CGFloat kScale = [[self snapshotCache] snapshotScaleForDevice];
UIGraphicsBeginImageContextWithOptions(size, YES, kScale);
CGContext* context = UIGraphicsGetCurrentContext();
if (!context) {
NOTREACHED();
return nil;
}
// -drawViewHierarchyInRect:afterScreenUpdates:YES is buggy as of iOS 8.3.
// Using it afterScreenUpdates:YES creates unexpected GPU glitches, screen
// redraws during animations, broken pinch to dismiss on tablet, etc. For now
// only using this with WKWebView, which depends on -drawViewHierarchyInRect.
// TODO(justincohen): Remove this (and always use drawViewHierarchyInRect)
// once the iOS 8 bugs have been fixed.
BOOL useDrawViewHierarchy = ViewHierarchyContainsWKWebView(view);
BOOL snapshotSuccess = YES;
CGContextSaveGState(context);
CGContextTranslateCTM(context, -rect.origin.x, -rect.origin.y);
if (useDrawViewHierarchy) {
snapshotSuccess =
[view drawViewHierarchyInRect:view.bounds afterScreenUpdates:NO];
} else {
[[view layer] renderInContext:context];
}
if ([overlays count]) {
for (SnapshotOverlay* overlay in overlays) {
// Render the overlay view at the desired offset. It is achieved
// by shifting origin of context because view frame is ignored when
// drawing to context.
CGContextSaveGState(context);
CGContextTranslateCTM(context, 0, overlay.yOffset);
if (useDrawViewHierarchy) {
[overlay.view drawViewHierarchyInRect:overlay.view.bounds
afterScreenUpdates:YES];
} else {
[[overlay.view layer] renderInContext:context];
}
CGContextRestoreGState(context);
}
}
UIImage* image = nil;
if (snapshotSuccess)
image = UIGraphicsGetImageFromCurrentImageContext();
CGContextRestoreGState(context);
UIGraphicsEndImageContext();
return image;
}
- (void)retrieveImageForSessionID:(NSString*)sessionID
callback:(void (^)(UIImage*))callback {
[[self snapshotCache] retrieveImageForSessionID:sessionID callback:callback];
}
- (void)retrieveGreyImageForSessionID:(NSString*)sessionID
callback:(void (^)(UIImage*))callback {
[[self snapshotCache] retrieveGreyImageForSessionID:sessionID
callback:callback];
}
- (void)setImage:(UIImage*)image withSessionID:(NSString*)sessionID {
[[self snapshotCache] setImage:image withSessionID:sessionID];
}
- (void)removeImageWithSessionID:(NSString*)sessionID {
[[self snapshotCache] removeImageWithSessionID:sessionID];
}
- (void)greyImageForSessionID:(NSString*)sessionID
callback:(void (^)(UIImage*))callback {
[[self snapshotCache] greyImageForSessionID:sessionID callback:callback];
}
#pragma mark - Private methods.
- (SnapshotCache*)snapshotCache {
return SnapshotCacheFactory::GetForBrowserState(
ios::ChromeBrowserState::FromBrowserState(_webState->GetBrowserState()));
}
@end