81a20cc4bd8c139e9fa36b54c332ff9453f3a6b9
[WebKit-https.git] / Tools / WebKitTestRunner / mac / PlatformWebViewMac.mm
1 /*
2  * Copyright (C) 2010, 2013, 2015 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 "PlatformWebView.h"
28
29 #import "TestController.h"
30 #import "WebKitTestRunnerDraggingInfo.h"
31 #import <WebKit/WKImageCG.h>
32 #import <WebKit/WKPreferencesPrivate.h>
33 #import <WebKit/WKWebViewConfiguration.h>
34 #import <WebKit/WKWebViewPrivate.h>
35 #import <wtf/RetainPtr.h>
36
37 #if WK_API_ENABLED
38 @interface WKWebView (Details)
39 - (WKPageRef)_pageForTesting;
40 @end
41 #endif
42
43 using namespace WTR;
44
45 enum {
46     _NSBackingStoreUnbuffered = 3
47 };
48
49 @interface WebKitTestRunnerWindow : NSWindow {
50     PlatformWebView* _platformWebView;
51     NSPoint _fakeOrigin;
52 }
53 @property (nonatomic, assign) PlatformWebView* platformWebView;
54 @end
55
56 #if WK_API_ENABLED
57 @interface TestRunnerWKWebView : WKWebView
58 @end
59
60 @implementation TestRunnerWKWebView
61
62 - (void)dragImage:(NSImage *)anImage at:(NSPoint)viewLocation offset:(NSSize)initialOffset event:(NSEvent *)event pasteboard:(NSPasteboard *)pboard source:(id)sourceObj slideBack:(BOOL)slideFlag
63 {
64     RetainPtr<WebKitTestRunnerDraggingInfo> draggingInfo = adoptNS([[WebKitTestRunnerDraggingInfo alloc] initWithImage:anImage offset:initialOffset pasteboard:pboard source:sourceObj]);
65     [self draggingUpdated:draggingInfo.get()];
66 }
67
68 @end
69 #endif
70
71 @implementation WebKitTestRunnerWindow
72 @synthesize platformWebView = _platformWebView;
73
74 - (BOOL)isKeyWindow
75 {
76     return _platformWebView ? _platformWebView->windowIsKey() : YES;
77 }
78
79 - (void)setFrameOrigin:(NSPoint)point
80 {
81     _fakeOrigin = point;
82 }
83
84 - (void)setFrame:(NSRect)windowFrame display:(BOOL)displayViews animate:(BOOL)performAnimation
85 {
86     NSRect currentFrame = [super frame];
87
88     _fakeOrigin = windowFrame.origin;
89
90     [super setFrame:NSMakeRect(currentFrame.origin.x, currentFrame.origin.y, windowFrame.size.width, windowFrame.size.height) display:displayViews animate:performAnimation];
91 }
92
93 - (void)setFrame:(NSRect)windowFrame display:(BOOL)displayViews
94 {
95     NSRect currentFrame = [super frame];
96
97     _fakeOrigin = windowFrame.origin;
98
99     [super setFrame:NSMakeRect(currentFrame.origin.x, currentFrame.origin.y, windowFrame.size.width, windowFrame.size.height) display:displayViews];
100 }
101
102 - (NSRect)frameRespectingFakeOrigin
103 {
104     NSRect currentFrame = [self frame];
105     return NSMakeRect(_fakeOrigin.x, _fakeOrigin.y, currentFrame.size.width, currentFrame.size.height);
106 }
107 @end
108
109 @interface NSWindow ()
110
111 - (void)_setWindowResolution:(CGFloat)resolution displayIfChanged:(BOOL)displayIfChanged;
112
113 @end
114
115 namespace WTR {
116
117 PlatformWebView::PlatformWebView(WKWebViewConfiguration* configuration, const ViewOptions& options)
118     : m_windowIsKey(true)
119     , m_options(options)
120 {
121 #if WK_API_ENABLED
122     // FIXME: Not sure this is the best place for this; maybe we should have API to set this so we can do it from TestController?
123     if (m_options.useRemoteLayerTree)
124         [[NSUserDefaults standardUserDefaults] setValue:@YES forKey:@"WebKit2UseRemoteLayerTreeDrawingArea"];
125
126     RetainPtr<WKWebViewConfiguration> copiedConfiguration = adoptNS([configuration copy]);
127     WKPreferencesSetThreadedScrollingEnabled((WKPreferencesRef)[copiedConfiguration preferences], m_options.useThreadedScrolling);
128
129     NSRect rect = NSMakeRect(0, 0, TestController::viewWidth, TestController::viewHeight);
130     m_view = [[TestRunnerWKWebView alloc] initWithFrame:rect configuration:copiedConfiguration.get()];
131     [m_view _setWindowOcclusionDetectionEnabled:NO];
132
133     NSScreen *firstScreen = [[NSScreen screens] objectAtIndex:0];
134     NSRect windowRect = m_options.shouldShowWebView ? NSOffsetRect(rect, 100, 100) : NSOffsetRect(rect, -10000, [firstScreen frame].size.height - rect.size.height + 10000);
135     m_window = [[WebKitTestRunnerWindow alloc] initWithContentRect:windowRect styleMask:NSBorderlessWindowMask backing:(NSBackingStoreType)_NSBackingStoreUnbuffered defer:YES];
136     m_window.platformWebView = this;
137     [m_window setColorSpace:[firstScreen colorSpace]];
138     [m_window setCollectionBehavior:NSWindowCollectionBehaviorStationary];
139     [[m_window contentView] addSubview:m_view];
140     if (m_options.shouldShowWebView)
141         [m_window orderFront:nil];
142     else
143         [m_window orderBack:nil];
144     [m_window setReleasedWhenClosed:NO];
145 #endif
146 }
147
148 void PlatformWebView::resizeTo(unsigned width, unsigned height)
149 {
150     WKRect frame = windowFrame();
151     frame.size.width = width;
152     frame.size.height = height;
153     setWindowFrame(frame);
154 }
155
156 PlatformWebView::~PlatformWebView()
157 {
158     m_window.platformWebView = nullptr;
159     [m_window close];
160     [m_window release];
161     [m_view release];
162 }
163
164 WKPageRef PlatformWebView::page()
165 {
166 #if WK_API_ENABLED
167     return [m_view _pageForTesting];
168 #else
169     return nullptr;
170 #endif
171 }
172
173 void PlatformWebView::focus()
174 {
175     [m_window makeFirstResponder:platformView()];
176     setWindowIsKey(true);
177 }
178
179 WKRect PlatformWebView::windowFrame()
180 {
181     NSRect frame = [m_window frameRespectingFakeOrigin];
182
183     WKRect wkFrame;
184     wkFrame.origin.x = frame.origin.x;
185     wkFrame.origin.y = frame.origin.y;
186     wkFrame.size.width = frame.size.width;
187     wkFrame.size.height = frame.size.height;
188     return wkFrame;
189 }
190
191 void PlatformWebView::setWindowFrame(WKRect frame)
192 {
193     [m_window setFrame:NSMakeRect(frame.origin.x, frame.origin.y, frame.size.width, frame.size.height) display:YES];
194     [platformView() setFrame:NSMakeRect(0, 0, frame.size.width, frame.size.height)];
195 }
196
197 void PlatformWebView::didInitializeClients()
198 {
199     // Set a temporary 1x1 window frame to force a WindowAndViewFramesChanged notification. <rdar://problem/13380145>
200     forceWindowFramesChanged();
201 }
202
203 void PlatformWebView::addChromeInputField()
204 {
205     NSTextField *textField = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 100, 20)];
206     textField.tag = 1;
207     [[m_window contentView] addSubview:textField];
208     [textField release];
209
210     NSView *view = platformView();
211     [textField setNextKeyView:view];
212     [view setNextKeyView:textField];
213 }
214
215 void PlatformWebView::removeChromeInputField()
216 {
217     NSView *textField = [[m_window contentView] viewWithTag:1];
218     if (textField) {
219         [textField removeFromSuperview];
220         makeWebViewFirstResponder();
221     }
222 }
223
224 void PlatformWebView::makeWebViewFirstResponder()
225 {
226     [m_window makeFirstResponder:platformView()];
227 }
228
229 WKRetainPtr<WKImageRef> PlatformWebView::windowSnapshotImage()
230 {
231     [platformView() display];
232     CGWindowImageOption options = kCGWindowImageBoundsIgnoreFraming | kCGWindowImageShouldBeOpaque;
233
234     if ([m_window backingScaleFactor] == 1)
235         options |= kCGWindowImageNominalResolution;
236
237     RetainPtr<CGImageRef> windowSnapshotImage = adoptCF(CGWindowListCreateImage(CGRectNull, kCGWindowListOptionIncludingWindow, [m_window windowNumber], options));
238
239     // windowSnapshotImage will be in GenericRGB, as we've set the main display's color space to GenericRGB.
240     return adoptWK(WKImageCreateFromCGImage(windowSnapshotImage.get(), 0));
241 }
242
243 bool PlatformWebView::viewSupportsOptions(const ViewOptions& options) const
244 {
245     if (m_options.useThreadedScrolling != options.useThreadedScrolling)
246         return false;
247
248     return true;
249 }
250
251 void PlatformWebView::changeWindowScaleIfNeeded(float newScale)
252 {
253     CGFloat currentScale = [m_window backingScaleFactor];
254     if (currentScale == newScale)
255         return;
256     [m_window _setWindowResolution:newScale displayIfChanged:YES];
257     // Instead of re-constructing the current window, let's fake resize it to ensure that the scale change gets picked up.
258     forceWindowFramesChanged();
259     // Changing the scaling factor on the window does not trigger NSWindowDidChangeBackingPropertiesNotification. We need to send the notification manually.
260     RetainPtr<NSMutableDictionary> notificationUserInfo = adoptNS([[NSMutableDictionary alloc] initWithCapacity:1]);
261     [notificationUserInfo setObject:[NSNumber numberWithDouble:currentScale] forKey:NSBackingPropertyOldScaleFactorKey];
262     [[NSNotificationCenter defaultCenter] postNotificationName:NSWindowDidChangeBackingPropertiesNotification object:m_window userInfo:notificationUserInfo.get()];
263 }
264
265 void PlatformWebView::forceWindowFramesChanged()
266 {
267     WKRect wkFrame = windowFrame();
268     [m_window setFrame:NSMakeRect(0, 0, 1, 1) display:YES];
269     setWindowFrame(wkFrame);
270 }
271
272 void PlatformWebView::setNavigationGesturesEnabled(bool enabled)
273 {
274 #if WK_API_ENABLED
275     [platformView() setAllowsBackForwardNavigationGestures:enabled];
276 #endif
277 }
278
279 } // namespace WTR