Add zoom support to WebKit2 API
[WebKit.git] / WebKitTools / MiniBrowser / mac / BrowserWindowController.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 "BrowserWindowController.h"
27
28 #import <WebKit2/WKPagePrivate.h>
29 #import <WebKit2/WKStringCF.h>
30 #import <WebKit2/WKURLCF.h>
31
32 @interface BrowserWindowController ()
33 - (void)didStartProgress;
34 - (void)didChangeProgress:(double)value;
35 - (void)didFinishProgress;
36 - (void)didStartProvisionalLoadForFrame:(WKFrameRef)frame;
37 - (void)didCommitLoadForFrame:(WKFrameRef)frame;
38 - (void)didReceiveServerRedirectForProvisionalLoadForFrame:(WKFrameRef)frame;
39 - (void)didFailProvisionalLoadWithErrorForFrame:(WKFrameRef)frame;
40 - (void)didFailLoadWithErrorForFrame:(WKFrameRef)frame;
41 @end
42
43 @implementation BrowserWindowController
44
45 - (id)initWithPageNamespace:(WKPageNamespaceRef)pageNamespace
46 {
47     if ((self = [super initWithWindowNibName:@"BrowserWindow"])) {
48         _pageNamespace = WKRetain(pageNamespace);
49         _zoomTextOnly = NO;
50     }
51     
52     return self;
53 }
54
55 - (void)dealloc
56 {
57     assert(!_pageNamespace);
58     [super dealloc];
59 }
60
61 - (IBAction)fetch:(id)sender
62 {
63     CFURLRef cfURL = CFURLCreateWithString(0, (CFStringRef)[urlText stringValue], 0);
64     WKURLRef url = WKURLCreateWithCFURL(cfURL);
65     CFRelease(cfURL);
66
67     WKPageLoadURL(_webView.pageRef, url);
68     WKRelease(url);
69 }
70
71 - (IBAction)showHideWebView:(id)sender
72 {
73     BOOL hidden = ![_webView isHidden];
74     
75     [_webView setHidden:hidden];
76 }
77
78 - (IBAction)removeReinsertWebView:(id)sender
79 {
80     if ([_webView window]) {
81         [_webView retain];
82         [_webView removeFromSuperview]; 
83     } else {
84         [containerView addSubview:_webView];
85         [_webView release];
86     }
87 }
88
89 - (BOOL)validateMenuItem:(NSMenuItem *)menuItem
90 {
91     SEL action = [menuItem action];
92
93     if (action == @selector(zoomIn:))
94         return [self canZoomIn];
95     if (action == @selector(zoomOut:))
96         return [self canZoomOut];
97     if (action == @selector(resetZoom:))
98         return [self canResetZoom];
99
100     if (action == @selector(showHideWebView:))
101         [menuItem setTitle:[_webView isHidden] ? @"Show Web View" : @"Hide Web View"];
102     else if (action == @selector(removeReinsertWebView:))
103         [menuItem setTitle:[_webView window] ? @"Remove Web View" : @"Insert Web View"];
104     else if (action == @selector(toggleZoomMode:))
105         [menuItem setState:_zoomTextOnly ? NSOnState : NSOffState];
106     return YES;
107 }
108
109 - (IBAction)reload:(id)sender
110 {
111     WKPageReload(_webView.pageRef);
112 }
113
114 - (IBAction)forceRepaint:(id)sender
115 {
116     [_webView setNeedsDisplay:YES];
117 }
118
119 - (IBAction)goBack:(id)sender
120 {
121     WKPageGoBack(_webView.pageRef);
122 }
123
124 - (IBAction)goForward:(id)sender
125 {
126     WKPageGoForward(_webView.pageRef);
127 }
128
129 - (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item
130 {
131     SEL action = [item action];
132
133     if (action == @selector(goBack:))
134         return _webView && WKPageCanGoBack(_webView.pageRef);
135     
136     if (action == @selector(goForward:))
137         return _webView && WKPageCanGoForward(_webView.pageRef);
138     
139     return YES;
140 }
141
142 - (void)validateToolbar
143 {
144     [toolbar validateVisibleItems];
145 }
146
147 - (BOOL)windowShouldClose:(id)sender
148 {
149     LOG(@"windowShouldClose");
150     BOOL canCloseImmediately = WKPageTryClose(_webView.pageRef);
151     return canCloseImmediately;
152 }
153
154 - (void)windowWillClose:(NSNotification *)notification
155 {
156     WKRelease(_pageNamespace);
157     _pageNamespace = 0;
158 }
159
160 - (void)applicationTerminating
161 {
162     WKPageClose(_webView.pageRef);
163     WKRelease(_webView.pageRef);
164 }
165
166 #define DefaultMinimumZoomFactor (.5)
167 #define DefaultMaximumZoomFactor (3.0)
168 #define DefaultZoomFactorRatio (1.2)
169
170 - (double)currentZoomFactor
171 {
172     return _zoomTextOnly ? WKPageGetTextZoomFactor(_webView.pageRef) : WKPageGetPageZoomFactor(_webView.pageRef);
173 }
174
175 - (void)setCurrentZoomFactor:(double)factor
176 {
177     _zoomTextOnly ? WKPageSetTextZoomFactor(_webView.pageRef, factor) : WKPageSetPageZoomFactor(_webView.pageRef, factor);
178 }
179
180 - (BOOL)canZoomIn
181 {
182     return [self currentZoomFactor] * DefaultZoomFactorRatio < DefaultMaximumZoomFactor;
183 }
184
185 - (void)zoomIn:(id)sender;
186 {
187     if (![self canZoomIn])
188         return;
189
190     double factor = [self currentZoomFactor] * DefaultZoomFactorRatio;
191     [self setCurrentZoomFactor:factor];
192 }
193
194 - (BOOL)canZoomOut
195 {
196     return [self currentZoomFactor] / DefaultZoomFactorRatio > DefaultMinimumZoomFactor;
197 }
198
199 - (void)zoomOut:(id)sender;
200 {
201     if (![self canZoomIn])
202         return;
203
204     double factor = [self currentZoomFactor] / DefaultZoomFactorRatio;
205     [self setCurrentZoomFactor:factor];
206 }
207
208 - (BOOL)canResetZoom
209 {
210     return _zoomTextOnly ? (WKPageGetTextZoomFactor(_webView.pageRef) != 1) : (WKPageGetPageZoomFactor(_webView.pageRef) != 1);
211 }
212
213 - (void)resetZoom:(id)sender;
214 {
215     if (![self canResetZoom])
216         return;
217
218     if (_zoomTextOnly)
219         WKPageSetTextZoomFactor(_webView.pageRef, 1);
220     else
221         WKPageSetPageZoomFactor(_webView.pageRef, 1);
222 }
223
224 - (IBAction)toggleZoomMode:(id)sender
225 {
226     if (_zoomTextOnly) {
227         _zoomTextOnly = NO;
228         double currentTextZoom = WKPageGetTextZoomFactor(_webView.pageRef);
229         WKPageSetPageAndTextZoomFactors(_webView.pageRef, currentTextZoom, 1);
230     } else {
231         _zoomTextOnly = YES;
232         double currentPageZoom = WKPageGetPageZoomFactor(_webView.pageRef);
233         WKPageSetPageAndTextZoomFactors(_webView.pageRef, 1, currentPageZoom);
234     }
235 }
236
237 #pragma mark Loader Client Callbacks
238
239 static void didStartProvisionalLoadForFrame(WKPageRef page, WKFrameRef frame, const void *clientInfo)
240 {
241     [(BrowserWindowController *)clientInfo didStartProvisionalLoadForFrame:frame];
242 }
243
244 static void didReceiveServerRedirectForProvisionalLoadForFrame(WKPageRef page, WKFrameRef frame, const void *clientInfo)
245 {
246     [(BrowserWindowController *)clientInfo didReceiveServerRedirectForProvisionalLoadForFrame:frame];
247 }
248
249 static void didFailProvisionalLoadWithErrorForFrame(WKPageRef page, WKFrameRef frame, const void *clientInfo)
250 {
251     [(BrowserWindowController *)clientInfo didFailProvisionalLoadWithErrorForFrame:frame];
252 }
253
254 static void didCommitLoadForFrame(WKPageRef page, WKFrameRef frame, const void *clientInfo)
255 {
256     [(BrowserWindowController *)clientInfo didCommitLoadForFrame:frame];
257 }
258
259 static void didFinishDocumentLoadForFrame(WKPageRef page, WKFrameRef frame, const void *clientInfo)
260 {
261     LOG(@"didFinishDocumentLoadForFrame");
262 }
263
264 static void didFinishLoadForFrame(WKPageRef page, WKFrameRef frame, const void *clientInfo)
265 {
266     LOG(@"didFinishLoadForFrame");
267 }
268
269 static void didFailLoadWithErrorForFrame(WKPageRef page, WKFrameRef frame, const void *clientInfo)
270 {
271     [(BrowserWindowController *)clientInfo didFailLoadWithErrorForFrame:frame];
272 }
273
274 static void didReceiveTitleForFrame(WKPageRef page, WKStringRef title, WKFrameRef frame, const void *clientInfo)
275 {
276     CFStringRef cfTitle = WKStringCopyCFString(0, title);
277     LOG(@"didReceiveTitleForFrame \"%@\"", (NSString *)cfTitle);
278     CFRelease(cfTitle);
279 }
280
281 static void didFirstLayoutForFrame(WKPageRef page, WKFrameRef frame, const void *clientInfo)
282 {
283     LOG(@"didFirstLayoutForFrame");
284 }
285
286 static void didFirstVisuallyNonEmptyLayoutForFrame(WKPageRef page, WKFrameRef frame, const void *clientInfo)
287 {
288     LOG(@"didFirstVisuallyNonEmptyLayoutForFrame");
289 }
290
291 static void didStartProgress(WKPageRef page, const void *clientInfo)
292 {
293     [(BrowserWindowController *)clientInfo didStartProgress];
294 }
295
296 static void didChangeProgress(WKPageRef page, const void *clientInfo)
297 {
298     [(BrowserWindowController *)clientInfo didChangeProgress:WKPageGetEstimatedProgress(page)];
299 }
300
301 static void didFinishProgress(WKPageRef page, const void *clientInfo)
302 {
303     [(BrowserWindowController *)clientInfo didFinishProgress];
304 }
305
306 static void didBecomeUnresponsive(WKPageRef page, const void *clientInfo)
307 {
308     LOG(@"didBecomeUnresponsive");
309 }
310
311 static void didBecomeResponsive(WKPageRef page, const void *clientInfo)
312 {
313     LOG(@"didBecomeResponsive");
314 }
315
316 static void processDidExit(WKPageRef page, const void *clientInfo)
317 {
318     LOG(@"processDidExit");
319 }
320
321 static void didChangeBackForwardList(WKPageRef page, const void *clientInfo)
322 {
323     [(BrowserWindowController *)clientInfo validateToolbar];
324 }
325
326 #pragma mark Policy Client Callbacks
327
328 static void decidePolicyForNavigationAction(WKPageRef page, WKFrameNavigationType navigationType, WKEventModifiers modifiers, WKURLRef url, WKFrameRef frame, WKFramePolicyListenerRef listener, const void *clientInfo)
329 {
330     LOG(@"decidePolicyForNavigationAction");
331     WKFramePolicyListenerUse(listener);
332 }
333
334 static void decidePolicyForNewWindowAction(WKPageRef page, WKFrameNavigationType navigationType, WKEventModifiers modifiers, WKURLRef url, WKFrameRef frame, WKFramePolicyListenerRef listener, const void *clientInfo)
335 {
336     LOG(@"decidePolicyForNewWindowAction");
337     WKFramePolicyListenerUse(listener);
338 }
339
340 static void decidePolicyForMIMEType(WKPageRef page, WKStringRef MIMEType, WKURLRef url, WKFrameRef frame, WKFramePolicyListenerRef listener, const void *clientInfo)
341 {
342     WKFramePolicyListenerUse(listener);
343 }
344
345 #pragma mark UI Client Callbacks
346
347 static WKPageRef createNewPage(WKPageRef page, const void* clientInfo)
348 {
349     LOG(@"createNewPage");
350     BrowserWindowController *controller = [[BrowserWindowController alloc] initWithPageNamespace:WKPageGetPageNamespace(page)];
351     [controller loadWindow];
352
353     return controller->_webView.pageRef;
354 }
355
356 static void showPage(WKPageRef page, const void *clientInfo)
357 {
358     LOG(@"showPage");
359     [[(BrowserWindowController *)clientInfo window] orderFront:nil];
360 }
361
362 static void closePage(WKPageRef page, const void *clientInfo)
363 {
364     LOG(@"closePage");
365     WKPageClose(page);
366     [[(BrowserWindowController *)clientInfo window] close];
367     WKRelease(page);
368 }
369
370 static void runJavaScriptAlert(WKPageRef page, WKStringRef message, WKFrameRef frame, const void* clientInfo)
371 {
372     NSAlert* alert = [[NSAlert alloc] init];
373
374     WKURLRef wkURL = WKFrameCopyURL(frame);
375     CFURLRef cfURL = WKURLCopyCFURL(0, wkURL);
376     WKRelease(wkURL);
377
378     [alert setMessageText:[NSString stringWithFormat:@"JavaScript alert dialog from %@.", [(NSURL *)cfURL absoluteString]]];
379     CFRelease(cfURL);
380
381     CFStringRef cfMessage = WKStringCopyCFString(0, message);
382     [alert setInformativeText:(NSString *)cfMessage];
383     CFRelease(cfMessage);
384
385     [alert addButtonWithTitle:@"OK"];
386
387     [alert runModal];
388     [alert release];
389 }
390
391 static bool runJavaScriptConfirm(WKPageRef page, WKStringRef message, WKFrameRef frame, const void* clientInfo)
392 {
393     NSAlert* alert = [[NSAlert alloc] init];
394
395     WKURLRef wkURL = WKFrameCopyURL(frame);
396     CFURLRef cfURL = WKURLCopyCFURL(0, wkURL);
397     WKRelease(wkURL);
398
399     [alert setMessageText:[NSString stringWithFormat:@"JavaScript confirm dialog from %@.", [(NSURL *)cfURL absoluteString]]];
400     CFRelease(cfURL);
401
402     CFStringRef cfMessage = WKStringCopyCFString(0, message);
403     [alert setInformativeText:(NSString *)cfMessage];
404     CFRelease(cfMessage);
405
406     [alert addButtonWithTitle:@"OK"];
407     [alert addButtonWithTitle:@"Cancel"];
408
409     NSInteger button = [alert runModal];
410     [alert release];
411
412     return button == NSAlertFirstButtonReturn;
413 }
414
415 static WKStringRef runJavaScriptPrompt(WKPageRef page, WKStringRef message, WKStringRef defaultValue, WKFrameRef frame, const void* clientInfo)
416 {
417     NSAlert* alert = [[NSAlert alloc] init];
418
419     WKURLRef wkURL = WKFrameCopyURL(frame);
420     CFURLRef cfURL = WKURLCopyCFURL(0, wkURL);
421     WKRelease(wkURL);
422
423     [alert setMessageText:[NSString stringWithFormat:@"JavaScript prompt dialog from %@.", [(NSURL *)cfURL absoluteString]]];
424     CFRelease(cfURL);
425
426     CFStringRef cfMessage = WKStringCopyCFString(0, message);
427     [alert setInformativeText:(NSString *)cfMessage];
428     CFRelease(cfMessage);
429
430     [alert addButtonWithTitle:@"OK"];
431     [alert addButtonWithTitle:@"Cancel"];
432
433     NSTextField* input = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 200, 24)];
434     CFStringRef cfDefaultValue = WKStringCopyCFString(0, defaultValue);
435     [input setStringValue:(NSString *)cfDefaultValue];
436     CFRelease(cfDefaultValue);
437
438     [alert setAccessoryView:input];
439
440     NSInteger button = [alert runModal];
441
442     NSString* result = nil;
443     if (button == NSAlertFirstButtonReturn) {
444         [input validateEditing];
445         result = [input stringValue];
446     }
447
448     [alert release];
449
450     if (!result)
451         return 0;
452     return WKStringCreateWithCFString((CFStringRef)result);
453 }
454
455 - (void)awakeFromNib
456 {
457     _webView = [[WKView alloc] initWithFrame:[containerView frame] pageNamespaceRef:_pageNamespace];
458
459     [containerView addSubview:_webView];
460     [_webView setFrame:[containerView frame]];
461     
462     [_webView setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)];
463     
464     WKPageLoaderClient loadClient = {
465         0,      /* version */
466         self,   /* clientInfo */
467         didStartProvisionalLoadForFrame,
468         didReceiveServerRedirectForProvisionalLoadForFrame,
469         didFailProvisionalLoadWithErrorForFrame,
470         didCommitLoadForFrame,
471         didFinishDocumentLoadForFrame,
472         didFinishLoadForFrame,
473         didFailLoadWithErrorForFrame,
474         didReceiveTitleForFrame,
475         didFirstLayoutForFrame,
476         didFirstVisuallyNonEmptyLayoutForFrame,
477         didStartProgress,
478         didChangeProgress,
479         didFinishProgress,
480         didBecomeUnresponsive,
481         didBecomeResponsive,
482         processDidExit,
483         didChangeBackForwardList
484     };
485     WKPageSetPageLoaderClient(_webView.pageRef, &loadClient);
486     
487     WKPagePolicyClient policyClient = {
488         0,          /* version */
489         self,       /* clientInfo */
490         decidePolicyForNavigationAction,
491         decidePolicyForNewWindowAction,
492         decidePolicyForMIMEType
493     };
494     WKPageSetPagePolicyClient(_webView.pageRef, &policyClient);
495
496     WKPageUIClient uiClient = {
497         0,          /* version */
498         self,       /* clientInfo */
499         createNewPage,
500         showPage,
501         closePage,
502         runJavaScriptAlert,
503         runJavaScriptConfirm,
504         runJavaScriptPrompt,
505         0           /* contentsSizeChanged */
506     };
507     WKPageSetPageUIClient(_webView.pageRef, &uiClient);
508 }
509
510 - (void)didStartProgress
511 {
512     [progressIndicator setDoubleValue:0.0];
513     [progressIndicator setHidden:NO];
514 }
515
516 - (void)didChangeProgress:(double)value
517 {
518     [progressIndicator setDoubleValue:value];
519 }
520
521 - (void)didFinishProgress
522 {
523     [progressIndicator setHidden:YES];
524     [progressIndicator setDoubleValue:1.0];
525 }
526
527 - (void)updateProvisionalURLForFrame:(WKFrameRef)frame
528 {
529     WKURLRef url = WKFrameCopyProvisionalURL(frame);
530     if (!url)
531         return;
532
533     CFURLRef cfSourceURL = WKURLCopyCFURL(0, url);
534     WKRelease(url);
535
536     [urlText setStringValue:(NSString*)CFURLGetString(cfSourceURL)];
537     CFRelease(cfSourceURL);
538 }
539
540 - (void)didStartProvisionalLoadForFrame:(WKFrameRef)frame
541 {
542     if (!WKFrameIsMainFrame(frame))
543         return;
544
545     [self updateProvisionalURLForFrame:frame];
546 }
547
548 - (void)didReceiveServerRedirectForProvisionalLoadForFrame:(WKFrameRef)frame
549 {
550     if (!WKFrameIsMainFrame(frame))
551         return;
552
553     [self updateProvisionalURLForFrame:frame];
554 }
555
556 - (void)didFailProvisionalLoadWithErrorForFrame:(WKFrameRef)frame
557 {
558     if (!WKFrameIsMainFrame(frame))
559         return;
560
561     [self updateProvisionalURLForFrame:frame];
562 }
563
564 - (void)didFailLoadWithErrorForFrame:(WKFrameRef)frame
565 {
566     if (!WKFrameIsMainFrame(frame))
567         return;
568
569     [self updateProvisionalURLForFrame:frame];
570 }
571
572 - (void)didCommitLoadForFrame:(WKFrameRef)frame
573 {
574 }
575
576 - (void)loadURLString:(NSString *)urlString
577 {
578     // FIXME: We shouldn't have to set the url text here.
579     [urlText setStringValue:urlString];
580     [self fetch:nil];
581 }
582
583 @end