Upstreaming browser/ui/uikit_ui_util from iOS.
[chromium-blink-merge.git] / ios / chrome / browser / snapshots / snapshot_manager.mm
blob557e9d5b3723689c29985861bba5222cb468a929
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"
14 namespace {
15 // Returns YES if |view| or any view it contains is a WKWebView.
16 BOOL ViewHierarchyContainsWKWebView(UIView* view) {
17   if ([view isKindOfClass:[WKWebView class]])
18     return YES;
19   for (UIView* subview in view.subviews) {
20     if (ViewHierarchyContainsWKWebView(subview))
21       return YES;
22   }
23   return NO;
25 }  // namespace
27 @implementation SnapshotManager
29 - (UIImage*)generateSnapshotForView:(UIView*)view
30                            withRect:(CGRect)rect
31                            overlays:(NSArray*)overlays {
32   DCHECK(view);
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();
41   if (!context) {
42     NOTREACHED();
43     return nil;
44   }
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];
58   } else {
59     [[view layer] renderInContext:context];
60   }
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];
75         }
76       } else {
77         [[overlay.view layer] renderInContext:context];
78       }
79       CGContextRestoreGState(context);
80     }
81   }
82   UIImage* image = UIGraphicsGetImageFromCurrentImageContext();
83   CGContextRestoreGState(context);
84   UIGraphicsEndImageContext();
85   return image;
88 - (void)retrieveImageForSessionID:(NSString*)sessionID
89                          callback:(void (^)(UIImage*))callback {
90   [[SnapshotCache sharedInstance] retrieveImageForSessionID:sessionID
91                                                    callback:callback];
94 - (void)retrieveGreyImageForSessionID:(NSString*)sessionID
95                              callback:(void (^)(UIImage*))callback {
96   [[SnapshotCache sharedInstance] retrieveGreyImageForSessionID:sessionID
97                                                        callback:callback];
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
111                                                callback:callback];
114 @end