Implement WebKit2 callback equivalent to - [WebUIDelegate webView:setStatusText:]
[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 - (IBAction)dumpSourceToConsole:(id)sender
238 {
239     WKPageGetSourceForFrame_b(_webView.pageRef, WKPageGetMainFrame(_webView.pageRef), ^(WKStringRef result, WKErrorRef error) {
240         CFStringRef cfResult = WKStringCopyCFString(0, result);
241         LOG(@"Main frame source\n \"%@\"", (NSString *)cfResult);
242         CFRelease(cfResult);
243     });
244 }
245
246 #pragma mark Loader Client Callbacks
247
248 static void didStartProvisionalLoadForFrame(WKPageRef page, WKFrameRef frame, const void *clientInfo)
249 {
250     [(BrowserWindowController *)clientInfo didStartProvisionalLoadForFrame:frame];
251 }
252
253 static void didReceiveServerRedirectForProvisionalLoadForFrame(WKPageRef page, WKFrameRef frame, const void *clientInfo)
254 {
255     [(BrowserWindowController *)clientInfo didReceiveServerRedirectForProvisionalLoadForFrame:frame];
256 }
257
258 static void didFailProvisionalLoadWithErrorForFrame(WKPageRef page, WKFrameRef frame, const void *clientInfo)
259 {
260     [(BrowserWindowController *)clientInfo didFailProvisionalLoadWithErrorForFrame:frame];
261 }
262
263 static void didCommitLoadForFrame(WKPageRef page, WKFrameRef frame, const void *clientInfo)
264 {
265     [(BrowserWindowController *)clientInfo didCommitLoadForFrame:frame];
266 }
267
268 static void didFinishDocumentLoadForFrame(WKPageRef page, WKFrameRef frame, const void *clientInfo)
269 {
270     LOG(@"didFinishDocumentLoadForFrame");
271 }
272
273 static void didFinishLoadForFrame(WKPageRef page, WKFrameRef frame, const void *clientInfo)
274 {
275     LOG(@"didFinishLoadForFrame");
276 }
277
278 static void didFailLoadWithErrorForFrame(WKPageRef page, WKFrameRef frame, const void *clientInfo)
279 {
280     [(BrowserWindowController *)clientInfo didFailLoadWithErrorForFrame:frame];
281 }
282
283 static void didReceiveTitleForFrame(WKPageRef page, WKStringRef title, WKFrameRef frame, const void *clientInfo)
284 {
285     CFStringRef cfTitle = WKStringCopyCFString(0, title);
286     LOG(@"didReceiveTitleForFrame \"%@\"", (NSString *)cfTitle);
287     CFRelease(cfTitle);
288 }
289
290 static void didFirstLayoutForFrame(WKPageRef page, WKFrameRef frame, const void *clientInfo)
291 {
292     LOG(@"didFirstLayoutForFrame");
293 }
294
295 static void didFirstVisuallyNonEmptyLayoutForFrame(WKPageRef page, WKFrameRef frame, const void *clientInfo)
296 {
297     LOG(@"didFirstVisuallyNonEmptyLayoutForFrame");
298 }
299
300 static void didStartProgress(WKPageRef page, const void *clientInfo)
301 {
302     [(BrowserWindowController *)clientInfo didStartProgress];
303 }
304
305 static void didChangeProgress(WKPageRef page, const void *clientInfo)
306 {
307     [(BrowserWindowController *)clientInfo didChangeProgress:WKPageGetEstimatedProgress(page)];
308 }
309
310 static void didFinishProgress(WKPageRef page, const void *clientInfo)
311 {
312     [(BrowserWindowController *)clientInfo didFinishProgress];
313 }
314
315 static void didBecomeUnresponsive(WKPageRef page, const void *clientInfo)
316 {
317     LOG(@"didBecomeUnresponsive");
318 }
319
320 static void didBecomeResponsive(WKPageRef page, const void *clientInfo)
321 {
322     LOG(@"didBecomeResponsive");
323 }
324
325 static void processDidExit(WKPageRef page, const void *clientInfo)
326 {
327     LOG(@"processDidExit");
328 }
329
330 static void didChangeBackForwardList(WKPageRef page, const void *clientInfo)
331 {
332     [(BrowserWindowController *)clientInfo validateToolbar];
333 }
334
335 #pragma mark Policy Client Callbacks
336
337 static void decidePolicyForNavigationAction(WKPageRef page, WKFrameNavigationType navigationType, WKEventModifiers modifiers, WKURLRef url, WKFrameRef frame, WKFramePolicyListenerRef listener, const void *clientInfo)
338 {
339     LOG(@"decidePolicyForNavigationAction");
340     WKFramePolicyListenerUse(listener);
341 }
342
343 static void decidePolicyForNewWindowAction(WKPageRef page, WKFrameNavigationType navigationType, WKEventModifiers modifiers, WKURLRef url, WKFrameRef frame, WKFramePolicyListenerRef listener, const void *clientInfo)
344 {
345     LOG(@"decidePolicyForNewWindowAction");
346     WKFramePolicyListenerUse(listener);
347 }
348
349 static void decidePolicyForMIMEType(WKPageRef page, WKStringRef MIMEType, WKURLRef url, WKFrameRef frame, WKFramePolicyListenerRef listener, const void *clientInfo)
350 {
351     WKFramePolicyListenerUse(listener);
352 }
353
354 #pragma mark UI Client Callbacks
355
356 static WKPageRef createNewPage(WKPageRef page, const void* clientInfo)
357 {
358     LOG(@"createNewPage");
359     BrowserWindowController *controller = [[BrowserWindowController alloc] initWithPageNamespace:WKPageGetPageNamespace(page)];
360     [controller loadWindow];
361
362     return controller->_webView.pageRef;
363 }
364
365 static void showPage(WKPageRef page, const void *clientInfo)
366 {
367     LOG(@"showPage");
368     [[(BrowserWindowController *)clientInfo window] orderFront:nil];
369 }
370
371 static void closePage(WKPageRef page, const void *clientInfo)
372 {
373     LOG(@"closePage");
374     WKPageClose(page);
375     [[(BrowserWindowController *)clientInfo window] close];
376     WKRelease(page);
377 }
378
379 static void runJavaScriptAlert(WKPageRef page, WKStringRef message, WKFrameRef frame, const void* clientInfo)
380 {
381     NSAlert* alert = [[NSAlert alloc] init];
382
383     WKURLRef wkURL = WKFrameCopyURL(frame);
384     CFURLRef cfURL = WKURLCopyCFURL(0, wkURL);
385     WKRelease(wkURL);
386
387     [alert setMessageText:[NSString stringWithFormat:@"JavaScript alert dialog from %@.", [(NSURL *)cfURL absoluteString]]];
388     CFRelease(cfURL);
389
390     CFStringRef cfMessage = WKStringCopyCFString(0, message);
391     [alert setInformativeText:(NSString *)cfMessage];
392     CFRelease(cfMessage);
393
394     [alert addButtonWithTitle:@"OK"];
395
396     [alert runModal];
397     [alert release];
398 }
399
400 static bool runJavaScriptConfirm(WKPageRef page, WKStringRef message, WKFrameRef frame, const void* clientInfo)
401 {
402     NSAlert* alert = [[NSAlert alloc] init];
403
404     WKURLRef wkURL = WKFrameCopyURL(frame);
405     CFURLRef cfURL = WKURLCopyCFURL(0, wkURL);
406     WKRelease(wkURL);
407
408     [alert setMessageText:[NSString stringWithFormat:@"JavaScript confirm dialog from %@.", [(NSURL *)cfURL absoluteString]]];
409     CFRelease(cfURL);
410
411     CFStringRef cfMessage = WKStringCopyCFString(0, message);
412     [alert setInformativeText:(NSString *)cfMessage];
413     CFRelease(cfMessage);
414
415     [alert addButtonWithTitle:@"OK"];
416     [alert addButtonWithTitle:@"Cancel"];
417
418     NSInteger button = [alert runModal];
419     [alert release];
420
421     return button == NSAlertFirstButtonReturn;
422 }
423
424 static WKStringRef runJavaScriptPrompt(WKPageRef page, WKStringRef message, WKStringRef defaultValue, WKFrameRef frame, const void* clientInfo)
425 {
426     NSAlert* alert = [[NSAlert alloc] init];
427
428     WKURLRef wkURL = WKFrameCopyURL(frame);
429     CFURLRef cfURL = WKURLCopyCFURL(0, wkURL);
430     WKRelease(wkURL);
431
432     [alert setMessageText:[NSString stringWithFormat:@"JavaScript prompt dialog from %@.", [(NSURL *)cfURL absoluteString]]];
433     CFRelease(cfURL);
434
435     CFStringRef cfMessage = WKStringCopyCFString(0, message);
436     [alert setInformativeText:(NSString *)cfMessage];
437     CFRelease(cfMessage);
438
439     [alert addButtonWithTitle:@"OK"];
440     [alert addButtonWithTitle:@"Cancel"];
441
442     NSTextField* input = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 200, 24)];
443     CFStringRef cfDefaultValue = WKStringCopyCFString(0, defaultValue);
444     [input setStringValue:(NSString *)cfDefaultValue];
445     CFRelease(cfDefaultValue);
446
447     [alert setAccessoryView:input];
448
449     NSInteger button = [alert runModal];
450
451     NSString* result = nil;
452     if (button == NSAlertFirstButtonReturn) {
453         [input validateEditing];
454         result = [input stringValue];
455     }
456
457     [alert release];
458
459     if (!result)
460         return 0;
461     return WKStringCreateWithCFString((CFStringRef)result);
462 }
463
464 static void setStatusText(WKPageRef page, WKStringRef text, const void* clientInfo)
465 {
466     LOG(@"setStatusText");
467 }
468
469 static void contentsSizeChanged(WKPageRef page, int width, int height, WKFrameRef frame, const void *clientInfo)
470 {
471     LOG(@"contentsSizeChanged");
472 }
473
474 - (void)awakeFromNib
475 {
476     _webView = [[WKView alloc] initWithFrame:[containerView frame] pageNamespaceRef:_pageNamespace];
477
478     [containerView addSubview:_webView];
479     [_webView setFrame:[containerView frame]];
480     
481     [_webView setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)];
482     
483     WKPageLoaderClient loadClient = {
484         0,      /* version */
485         self,   /* clientInfo */
486         didStartProvisionalLoadForFrame,
487         didReceiveServerRedirectForProvisionalLoadForFrame,
488         didFailProvisionalLoadWithErrorForFrame,
489         didCommitLoadForFrame,
490         didFinishDocumentLoadForFrame,
491         didFinishLoadForFrame,
492         didFailLoadWithErrorForFrame,
493         didReceiveTitleForFrame,
494         didFirstLayoutForFrame,
495         didFirstVisuallyNonEmptyLayoutForFrame,
496         didStartProgress,
497         didChangeProgress,
498         didFinishProgress,
499         didBecomeUnresponsive,
500         didBecomeResponsive,
501         processDidExit,
502         didChangeBackForwardList
503     };
504     WKPageSetPageLoaderClient(_webView.pageRef, &loadClient);
505     
506     WKPagePolicyClient policyClient = {
507         0,          /* version */
508         self,       /* clientInfo */
509         decidePolicyForNavigationAction,
510         decidePolicyForNewWindowAction,
511         decidePolicyForMIMEType
512     };
513     WKPageSetPagePolicyClient(_webView.pageRef, &policyClient);
514
515     WKPageUIClient uiClient = {
516         0,          /* version */
517         self,       /* clientInfo */
518         createNewPage,
519         showPage,
520         closePage,
521         runJavaScriptAlert,
522         runJavaScriptConfirm,
523         runJavaScriptPrompt,
524         setStatusText,
525         contentsSizeChanged
526     };
527     WKPageSetPageUIClient(_webView.pageRef, &uiClient);
528 }
529
530 - (void)didStartProgress
531 {
532     [progressIndicator setDoubleValue:0.0];
533     [progressIndicator setHidden:NO];
534 }
535
536 - (void)didChangeProgress:(double)value
537 {
538     [progressIndicator setDoubleValue:value];
539 }
540
541 - (void)didFinishProgress
542 {
543     [progressIndicator setHidden:YES];
544     [progressIndicator setDoubleValue:1.0];
545 }
546
547 - (void)updateProvisionalURLForFrame:(WKFrameRef)frame
548 {
549     WKURLRef url = WKFrameCopyProvisionalURL(frame);
550     if (!url)
551         return;
552
553     CFURLRef cfSourceURL = WKURLCopyCFURL(0, url);
554     WKRelease(url);
555
556     [urlText setStringValue:(NSString*)CFURLGetString(cfSourceURL)];
557     CFRelease(cfSourceURL);
558 }
559
560 - (void)didStartProvisionalLoadForFrame:(WKFrameRef)frame
561 {
562     if (!WKFrameIsMainFrame(frame))
563         return;
564
565     [self updateProvisionalURLForFrame:frame];
566 }
567
568 - (void)didReceiveServerRedirectForProvisionalLoadForFrame:(WKFrameRef)frame
569 {
570     if (!WKFrameIsMainFrame(frame))
571         return;
572
573     [self updateProvisionalURLForFrame:frame];
574 }
575
576 - (void)didFailProvisionalLoadWithErrorForFrame:(WKFrameRef)frame
577 {
578     if (!WKFrameIsMainFrame(frame))
579         return;
580
581     [self updateProvisionalURLForFrame:frame];
582 }
583
584 - (void)didFailLoadWithErrorForFrame:(WKFrameRef)frame
585 {
586     if (!WKFrameIsMainFrame(frame))
587         return;
588
589     [self updateProvisionalURLForFrame:frame];
590 }
591
592 - (void)didCommitLoadForFrame:(WKFrameRef)frame
593 {
594 }
595
596 - (void)loadURLString:(NSString *)urlString
597 {
598     // FIXME: We shouldn't have to set the url text here.
599     [urlText setStringValue:urlString];
600     [self fetch:nil];
601 }
602
603 @end