1 // Copyright 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #import "ios/chrome/browser/snapshots/snapshot_manager.h"
7 #import <QuartzCore/QuartzCore.h>
8 #import <WebKit/WebKit.h>
10 #include "base/logging.h"
11 #import "ios/chrome/browser/snapshots/snapshot_cache.h"
12 #import "ios/chrome/browser/snapshots/snapshot_overlay.h"
15 // Returns YES if |view| or any view it contains is a WKWebView.
16 BOOL ViewHierarchyContainsWKWebView(UIView* view) {
17 if ([view isKindOfClass:[WKWebView class]])
19 for (UIView* subview in view.subviews) {
20 if (ViewHierarchyContainsWKWebView(subview))
27 @implementation SnapshotManager
29 - (UIImage*)generateSnapshotForView:(UIView*)view
31 overlays:(NSArray*)overlays {
33 CGSize size = rect.size;
34 DCHECK(std::isnormal(size.width) && (size.width > 0))
35 << ": size.width=" << size.width;
36 DCHECK(std::isnormal(size.height) && (size.height > 0))
37 << ": size.height=" << size.height;
38 const CGFloat kScale = [SnapshotCache snapshotScaleForDevice];
39 UIGraphicsBeginImageContextWithOptions(size, YES, kScale);
40 CGContext* context = UIGraphicsGetCurrentContext();
46 // -drawViewHierarchyInRect:afterScreenUpdates:YES is buggy as of iOS 8.3.
47 // Using it afterScreenUpdates:YES creates unexpected GPU glitches, screen
48 // redraws during animations, broken pinch to dismiss on tablet, etc. For now
49 // only using this with WKWebView, which depends on -drawViewHierarchyInRect.
50 // TODO(justincohen): Remove this (and always use drawViewHierarchyInRect)
51 // once the iOS 8 bugs have been fixed.
52 BOOL useDrawViewHierarchy = ViewHierarchyContainsWKWebView(view);
54 CGContextSaveGState(context);
55 CGContextTranslateCTM(context, -rect.origin.x, -rect.origin.y);
56 if (useDrawViewHierarchy) {
57 [view drawViewHierarchyInRect:view.bounds afterScreenUpdates:YES];
59 [[view layer] renderInContext:context];
61 if ([overlays count]) {
62 for (SnapshotOverlay* overlay in overlays) {
63 // Render the overlay view at the desired offset. It is achieved
64 // by shifting origin of context because view frame is ignored when
65 // drawing to context.
66 CGContextSaveGState(context);
67 CGContextTranslateCTM(context, 0, overlay.yOffset);
68 if (useDrawViewHierarchy) {
69 CGRect overlayRect = overlay.view.bounds;
70 // TODO(jyquinn): The 0 check is needed for a UIKit crash on iOS 7. This
71 // can be removed once iOS 7 is dropped. crbug.com/421213
72 if (overlayRect.size.width > 0 && overlayRect.size.height > 0) {
73 [overlay.view drawViewHierarchyInRect:overlay.view.bounds
74 afterScreenUpdates:YES];
77 [[overlay.view layer] renderInContext:context];
79 CGContextRestoreGState(context);
82 UIImage* image = UIGraphicsGetImageFromCurrentImageContext();
83 CGContextRestoreGState(context);
84 UIGraphicsEndImageContext();
88 - (void)retrieveImageForSessionID:(NSString*)sessionID
89 callback:(void (^)(UIImage*))callback {
90 [[SnapshotCache sharedInstance] retrieveImageForSessionID:sessionID
94 - (void)retrieveGreyImageForSessionID:(NSString*)sessionID
95 callback:(void (^)(UIImage*))callback {
96 [[SnapshotCache sharedInstance] retrieveGreyImageForSessionID:sessionID
100 - (void)setImage:(UIImage*)img withSessionID:(NSString*)sessionID {
101 [[SnapshotCache sharedInstance] setImage:img withSessionID:sessionID];
104 - (void)removeImageWithSessionID:(NSString*)sessionID {
105 [[SnapshotCache sharedInstance] removeImageWithSessionID:sessionID];
108 - (void)greyImageForSessionID:(NSString*)sessionID
109 callback:(void (^)(UIImage*))callback {
110 [[SnapshotCache sharedInstance] greyImageForSessionID:sessionID