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