[WK2] Expose a few drawing/compositing settings on WKPreferences(Private)
[WebKit-https.git] / Tools / MiniBrowser / mac / WK2BrowserWindowController.m
1 /*
2  * Copyright (C) 2010 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 "WK2BrowserWindowController.h"
27
28 #if WK_API_ENABLED
29
30 #import "AppDelegate.h"
31 #import <WebKit/WKFrameInfo.h>
32 #import <WebKit/WKNavigationDelegate.h>
33 #import <WebKit/WKPreferencesPrivate.h>
34 #import <WebKit/WKUIDelegate.h>
35 #import <WebKit/WKWebView.h>
36 #import <WebKit/WKWebViewConfiguration.h>
37 #import <WebKit/WKWebViewPrivate.h>
38
39 static void* keyValueObservingContext = &keyValueObservingContext;
40 static NSString * const WebKit2UseRemoteLayerTreeDrawingAreaKey = @"WebKit2UseRemoteLayerTreeDrawingArea";
41
42 static NSString * const LayerBordersVisiblePreferenceKey = @"LayerBordersVisible";
43 static NSString * const TiledScrollingIndicatorVisiblePreferenceKey = @"TiledScrollingIndicatorVisibleKey";
44
45 @interface WK2BrowserWindowController () <WKNavigationDelegate, WKUIDelegate>
46 @end
47
48 @implementation WK2BrowserWindowController {
49     WKWebView *_webView;
50     BOOL _zoomTextOnly;
51 }
52
53 - (void)awakeFromNib
54 {
55     static WKWebViewConfiguration *configuration;
56     if (!configuration) {
57         configuration = [[WKWebViewConfiguration alloc] init];
58
59         configuration.preferences._tiledScrollingIndicatorVisible = [self tiledScrollingIndicatorVisible];
60         configuration.preferences._compositingBordersVisible = [self layerBordersVisible];
61         configuration.preferences._compositingRepaintCountersVisible = [self layerBordersVisible];
62     }
63     _webView = [[WKWebView alloc] initWithFrame:[containerView bounds] configuration:configuration];
64
65     _webView.allowsMagnification = YES;
66     _webView.allowsBackForwardNavigationGestures = YES;
67
68     [_webView setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)];
69     [containerView addSubview:_webView];
70
71     [progressIndicator bind:NSHiddenBinding toObject:_webView withKeyPath:@"loading" options:@{ NSValueTransformerNameBindingOption : NSNegateBooleanTransformerName }];
72     [progressIndicator bind:NSValueBinding toObject:_webView withKeyPath:@"estimatedProgress" options:nil];
73
74     [_webView addObserver:self forKeyPath:@"title" options:0 context:keyValueObservingContext];
75     [_webView addObserver:self forKeyPath:@"URL" options:0 context:keyValueObservingContext];
76
77     _webView.navigationDelegate = self;
78     _webView.UIDelegate = self;
79     
80     _zoomTextOnly = NO;
81 }
82
83 - (void)dealloc
84 {
85     [_webView removeObserver:self forKeyPath:@"title"];
86     [_webView removeObserver:self forKeyPath:@"URL"];
87     
88     [progressIndicator unbind:NSHiddenBinding];
89     [progressIndicator unbind:NSValueBinding];
90
91     [_webView release];
92
93     [super dealloc];
94 }
95
96 - (IBAction)fetch:(id)sender
97 {
98     [urlText setStringValue:[self addProtocolIfNecessary:[urlText stringValue]]];
99
100     [_webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:[urlText stringValue]]]];
101 }
102
103 - (IBAction)showHideWebView:(id)sender
104 {
105     BOOL hidden = ![_webView isHidden];
106     
107     [_webView setHidden:hidden];
108 }
109
110 - (IBAction)removeReinsertWebView:(id)sender
111 {
112     if ([_webView window]) {
113         [_webView retain];
114         [_webView removeFromSuperview]; 
115     } else {
116         [containerView addSubview:_webView];
117         [_webView release];
118     }
119 }
120
121 - (BOOL)validateMenuItem:(NSMenuItem *)menuItem
122 {
123     SEL action = [menuItem action];
124
125     if (action == @selector(zoomIn:))
126         return [self canZoomIn];
127     if (action == @selector(zoomOut:))
128         return [self canZoomOut];
129     if (action == @selector(resetZoom:))
130         return [self canResetZoom];
131     
132     // Disabled until missing WK2 functionality is exposed via API/SPI.
133     if (action == @selector(dumpSourceToConsole:)
134         || action == @selector(find:))
135         return NO;
136     
137     if (action == @selector(showHideWebView:))
138         [menuItem setTitle:[_webView isHidden] ? @"Show Web View" : @"Hide Web View"];
139     else if (action == @selector(removeReinsertWebView:))
140         [menuItem setTitle:[_webView window] ? @"Remove Web View" : @"Insert Web View"];
141     else if (action == @selector(toggleZoomMode:))
142         [menuItem setState:_zoomTextOnly ? NSOnState : NSOffState];
143     else if (action == @selector(togglePaginationMode:))
144         [menuItem setState:[self isPaginated] ? NSOnState : NSOffState];
145     else if (action == @selector(toggleTransparentWindow:))
146         [menuItem setState:[[self window] isOpaque] ? NSOffState : NSOnState];
147     else if (action == @selector(toggleUISideCompositing:))
148         [menuItem setState:[self isUISideCompositingEnabled] ? NSOnState : NSOffState];
149     else if (action == @selector(toggleLayerBordersVisibility:))
150         [menuItem setState:[self layerBordersVisible] ? NSOnState : NSOffState];
151     else if (action == @selector(toggleTiledScrollingIndicatorVisibility:))
152         [menuItem setState:[self tiledScrollingIndicatorVisible] ? NSOnState : NSOffState];
153
154     return YES;
155 }
156
157 - (IBAction)reload:(id)sender
158 {
159     [_webView reload];
160 }
161
162 - (IBAction)forceRepaint:(id)sender
163 {
164     [_webView setNeedsDisplay:YES];
165 }
166
167 - (IBAction)goBack:(id)sender
168 {
169     [_webView goBack];
170 }
171
172 - (IBAction)goForward:(id)sender
173 {
174     [_webView goForward];
175 }
176
177 - (IBAction)toggleZoomMode:(id)sender
178 {
179     if (_zoomTextOnly) {
180         _zoomTextOnly = NO;
181         double currentTextZoom = _webView._textZoomFactor;
182         _webView._textZoomFactor = 1;
183         _webView._pageZoomFactor = currentTextZoom;
184     } else {
185         _zoomTextOnly = YES;
186         double currentPageZoom = _webView._pageZoomFactor;
187         _webView._textZoomFactor = currentPageZoom;
188         _webView._pageZoomFactor = 1;
189     }
190 }
191
192 - (IBAction)resetZoom:(id)sender
193 {
194     if (![self canResetZoom])
195         return;
196
197     if (_zoomTextOnly)
198         _webView._textZoomFactor = 1;
199     else
200         _webView._pageZoomFactor = 1;
201 }
202
203 - (BOOL)canResetZoom
204 {
205     return _zoomTextOnly ? (_webView._textZoomFactor != 1) : (_webView._pageZoomFactor != 1);
206 }
207
208 - (IBAction)dumpSourceToConsole:(id)sender
209 {
210 }
211
212 - (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item
213 {
214     SEL action = item.action;
215
216     if (action == @selector(goBack:) || action == @selector(goForward:))
217         return [_webView validateUserInterfaceItem:item];
218
219     return YES;
220 }
221
222 - (void)validateToolbar
223 {
224     [toolbar validateVisibleItems];
225 }
226
227 - (BOOL)windowShouldClose:(id)sender
228 {
229     return YES;
230 }
231
232 - (void)windowWillClose:(NSNotification *)notification
233 {
234     [(BrowserAppDelegate *)[NSApp delegate] browserWindowWillClose:[self window]];
235     [self autorelease];
236 }
237
238 - (void)applicationTerminating
239 {
240 }
241
242 #define DefaultMinimumZoomFactor (.5)
243 #define DefaultMaximumZoomFactor (3.0)
244 #define DefaultZoomFactorRatio (1.2)
245
246 - (CGFloat)currentZoomFactor
247 {
248     return _zoomTextOnly ? _webView._textZoomFactor : _webView._pageZoomFactor;
249 }
250
251 - (void)setCurrentZoomFactor:(CGFloat)factor
252 {
253     if (_zoomTextOnly)
254         _webView._textZoomFactor = factor;
255     else
256         _webView._pageZoomFactor = factor;
257 }
258
259 - (BOOL)canZoomIn
260 {
261     return self.currentZoomFactor * DefaultZoomFactorRatio < DefaultMaximumZoomFactor;
262 }
263
264 - (void)zoomIn:(id)sender
265 {
266     if (!self.canZoomIn)
267         return;
268
269     self.currentZoomFactor *= DefaultZoomFactorRatio;
270 }
271
272 - (BOOL)canZoomOut
273 {
274     return self.currentZoomFactor / DefaultZoomFactorRatio > DefaultMinimumZoomFactor;
275 }
276
277 - (void)zoomOut:(id)sender
278 {
279     if (!self.canZoomIn)
280         return;
281
282     self.currentZoomFactor /= DefaultZoomFactorRatio;
283 }
284
285 - (BOOL)isPaginated
286 {
287     return _webView._paginationMode != _WKPaginationModeUnpaginated;
288 }
289
290 - (IBAction)togglePaginationMode:(id)sender
291 {
292     if (self.isPaginated)
293         _webView._paginationMode = _WKPaginationModeUnpaginated;
294     else {
295         _webView._paginationMode = _WKPaginationModeLeftToRight;
296         _webView._pageLength = _webView.bounds.size.width / 2;
297         _webView._gapBetweenPages = 10;
298     }
299 }
300
301 - (IBAction)toggleTransparentWindow:(id)sender
302 {
303     BOOL isTransparent = _webView._drawsTransparentBackground;
304     isTransparent = !isTransparent;
305
306     [[self window] setOpaque:!isTransparent];
307     [[self window] setHasShadow:!isTransparent];
308
309     _webView._drawsTransparentBackground = isTransparent;
310
311     [[self window] display];    
312 }
313
314 - (BOOL)isUISideCompositingEnabled
315 {
316     return [[NSUserDefaults standardUserDefaults] boolForKey:WebKit2UseRemoteLayerTreeDrawingAreaKey];
317 }
318
319 - (IBAction)toggleUISideCompositing:(id)sender
320 {
321     [[NSUserDefaults standardUserDefaults] setBool:![self isUISideCompositingEnabled] forKey:WebKit2UseRemoteLayerTreeDrawingAreaKey];
322 }
323
324 - (BOOL)layerBordersVisible
325 {
326     return [[NSUserDefaults standardUserDefaults] boolForKey:LayerBordersVisiblePreferenceKey];
327 }
328
329 - (IBAction)toggleLayerBordersVisibility:(id)sender
330 {
331     BOOL newState = ![self layerBordersVisible];
332     [[NSUserDefaults standardUserDefaults] setBool:newState forKey:LayerBordersVisiblePreferenceKey];
333     _webView.configuration.preferences._compositingBordersVisible = newState;
334     _webView.configuration.preferences._compositingRepaintCountersVisible = newState;
335 }
336
337 - (BOOL)tiledScrollingIndicatorVisible
338 {
339     return [[NSUserDefaults standardUserDefaults] boolForKey:TiledScrollingIndicatorVisiblePreferenceKey];
340 }
341
342 - (IBAction)toggleTiledScrollingIndicatorVisibility:(id)sender
343 {
344     BOOL newState = ![self tiledScrollingIndicatorVisible];
345     [[NSUserDefaults standardUserDefaults] setBool:newState forKey:TiledScrollingIndicatorVisiblePreferenceKey];
346     _webView.configuration.preferences._tiledScrollingIndicatorVisible = newState;
347 }
348
349 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
350 {
351     if (context != keyValueObservingContext || object != _webView)
352         return;
353
354     if ([keyPath isEqualToString:@"title"])
355         self.window.title = [_webView.title stringByAppendingFormat:@" [WK2, %d]", _webView._webProcessIdentifier];
356     else if ([keyPath isEqualToString:@"URL"])
357         [self updateTextFieldFromURL:_webView.URL];
358 }
359
360 - (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)())completionHandler
361 {
362     NSAlert* alert = [[NSAlert alloc] init];
363
364     [alert setMessageText:[NSString stringWithFormat:@"JavaScript alert dialog from %@.", [frame.request.URL absoluteString]]];
365     [alert setInformativeText:message];
366     [alert addButtonWithTitle:@"OK"];
367
368 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090
369     [alert beginSheetModalForWindow:self.window completionHandler:^void (NSModalResponse response) {
370         completionHandler();
371         [alert release];
372     }];
373 #endif
374 }
375
376 - (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler
377 {
378     NSAlert* alert = [[NSAlert alloc] init];
379
380     [alert setMessageText:[NSString stringWithFormat:@"JavaScript confirm dialog from %@.", [frame.request.URL  absoluteString]]];
381     [alert setInformativeText:message];
382     
383     [alert addButtonWithTitle:@"OK"];
384     [alert addButtonWithTitle:@"Cancel"];
385
386 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090
387     [alert beginSheetModalForWindow:self.window completionHandler:^void (NSModalResponse response) {
388         completionHandler(response == NSAlertFirstButtonReturn);
389         [alert release];
390     }];
391 #endif
392 }
393
394 - (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString *result))completionHandler
395 {
396     NSAlert* alert = [[NSAlert alloc] init];
397
398     [alert setMessageText:[NSString stringWithFormat:@"JavaScript prompt dialog from %@.", [frame.request.URL absoluteString]]];
399     [alert setInformativeText:prompt];
400     
401     [alert addButtonWithTitle:@"OK"];
402     [alert addButtonWithTitle:@"Cancel"];
403     
404     NSTextField* input = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 200, 24)];
405     [input setStringValue:defaultText];
406     [alert setAccessoryView:input];
407     
408 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090
409     [alert beginSheetModalForWindow:self.window completionHandler:^void (NSModalResponse response) {
410         [input validateEditing];
411         completionHandler(response == NSAlertFirstButtonReturn ? [input stringValue] : nil);
412         [alert release];
413     }];
414 #endif
415 }
416
417 - (void)updateTextFieldFromURL:(NSURL *)URL
418 {
419     if (!URL)
420         return;
421
422     if (!URL.absoluteString.length)
423         return;
424
425     urlText.stringValue = [URL absoluteString];
426 }
427
428 - (void)loadURLString:(NSString *)urlString
429 {
430     // FIXME: We shouldn't have to set the url text here.
431     [urlText setStringValue:urlString];
432     [self fetch:nil];
433 }
434
435 - (IBAction)performFindPanelAction:(id)sender
436 {
437     [findPanelWindow makeKeyAndOrderFront:sender];
438 }
439
440 - (IBAction)find:(id)sender
441 {
442 }
443
444 #pragma mark WKNavigationDelegate
445
446 - (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler
447 {
448     LOG(@"decidePolicyForNavigationResponse");
449     decisionHandler(WKNavigationResponsePolicyAllow);
450 }
451
452 - (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation
453 {
454     LOG(@"didStartProvisionalNavigation: %@", navigation);
455 }
456
457 - (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation
458 {
459     LOG(@"didReceiveServerRedirectForProvisionalNavigation: %@", navigation);
460 }
461
462 - (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error
463 {
464     LOG(@"didFailProvisionalNavigation: %@navigation, error: %@", navigation, error);
465 }
466
467 - (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation
468 {
469     LOG(@"didCommitNavigation: %@", navigation);
470 }
471
472 - (void)webView:(WKWebView *)webView didFinishLoadingNavigation:(WKNavigation *)navigation
473 {
474     LOG(@"didFinishLoadingNavigation: %@", navigation);
475 }
476
477 - (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error
478 {
479     LOG(@"didFailNavigation: %@, error %@", navigation, error);
480 }
481
482 - (void)_webViewWebProcessDidCrash:(WKWebView *)webView
483 {
484     NSLog(@"WebContent process crashed; reloading");
485     [self reload:nil];
486 }
487
488 @end
489
490 #endif // WK_API_ENABLED