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