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