]> git.saurik.com Git - wxWidgets.git/blob - src/osx/webview_webkit.mm
c3a8e7458d99be5aeaa0e5230953a04800b7fa3e
[wxWidgets.git] / src / osx / webview_webkit.mm
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/osx/webview_webkit.mm
3 // Purpose: wxWebViewWebKit - embeddable web kit control,
4 // OS X implementation of web view component
5 // Author: Jethro Grassie / Kevin Ollivier / Marianne Gagnon
6 // Modified by:
7 // Created: 2004-4-16
8 // RCS-ID: $Id$
9 // Copyright: (c) Jethro Grassie / Kevin Ollivier / Marianne Gagnon
10 // Licence: wxWindows licence
11 /////////////////////////////////////////////////////////////////////////////
12
13 // http://developer.apple.com/mac/library/documentation/Cocoa/Reference/WebKit/Classes/WebView_Class/Reference/Reference.html
14
15 #include "wx/osx/webview_webkit.h"
16
17 #if wxUSE_WEBVIEW_WEBKIT && (defined(__WXOSX_COCOA__) \
18 || defined(__WXOSX_CARBON__))
19
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
22
23 #ifndef WX_PRECOMP
24 #include "wx/wx.h"
25 #endif
26
27 #ifdef __WXCOCOA__
28 #include "wx/cocoa/autorelease.h"
29 #else
30 #include "wx/osx/private.h"
31
32 #include <WebKit/WebKit.h>
33 #include <WebKit/HIWebView.h>
34 #include <WebKit/CarbonUtils.h>
35 #endif
36
37 #include <Foundation/NSURLError.h>
38
39 // FIXME: find cleaner way to find the wxWidgets ID of a webview than this hack
40 #include <map>
41 std::map<WebView*, wxWebViewWebKit*> wx_webviewctrls;
42
43 #define DEBUG_WEBKIT_SIZING 0
44
45 // ----------------------------------------------------------------------------
46 // macros
47 // ----------------------------------------------------------------------------
48
49 wxIMPLEMENT_DYNAMIC_CLASS(wxWebViewWebKit, wxWebView);
50
51 BEGIN_EVENT_TABLE(wxWebViewWebKit, wxControl)
52 #if defined(__WXMAC__) && wxOSX_USE_CARBON
53 EVT_SIZE(wxWebViewWebKit::OnSize)
54 #endif
55 END_EVENT_TABLE()
56
57 #if defined(__WXOSX__) && wxOSX_USE_CARBON
58
59 // ----------------------------------------------------------------------------
60 // Carbon Events handlers
61 // ----------------------------------------------------------------------------
62
63 // prototype for function in src/osx/carbon/nonownedwnd.cpp
64 void SetupMouseEvent( wxMouseEvent &wxevent , wxMacCarbonEvent &cEvent );
65
66 static const EventTypeSpec eventList[] =
67 {
68 //{ kEventClassControl, kEventControlTrack } ,
69 { kEventClassMouse, kEventMouseUp },
70 { kEventClassMouse, kEventMouseDown },
71 { kEventClassMouse, kEventMouseMoved },
72 { kEventClassMouse, kEventMouseDragged },
73
74 { kEventClassKeyboard, kEventRawKeyDown } ,
75 { kEventClassKeyboard, kEventRawKeyRepeat } ,
76 { kEventClassKeyboard, kEventRawKeyUp } ,
77 { kEventClassKeyboard, kEventRawKeyModifiersChanged } ,
78
79 { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent } ,
80 { kEventClassTextInput, kEventTextInputUpdateActiveInputArea } ,
81
82 #if DEBUG_WEBKIT_SIZING == 1
83 { kEventClassControl, kEventControlBoundsChanged } ,
84 #endif
85 };
86
87 // mix this in from window.cpp
88 pascal OSStatus wxMacUnicodeTextEventHandler(EventHandlerCallRef handler,
89 EventRef event, void *data) ;
90
91 // NOTE: This is mostly taken from KeyboardEventHandler in toplevel.cpp, but
92 // that expects the data pointer is a top-level window, so I needed to change
93 // that in this case. However, once 2.8 is out, we should factor out the common
94 // logic among the two functions and merge them.
95 static pascal OSStatus wxWebKitKeyEventHandler(EventHandlerCallRef handler,
96 EventRef event, void *data)
97 {
98 OSStatus result = eventNotHandledErr ;
99 wxMacCarbonEvent cEvent( event ) ;
100
101 wxWebViewWebKit* thisWindow = (wxWebViewWebKit*) data ;
102 wxWindow* focus = thisWindow ;
103
104 unsigned char charCode ;
105 wxChar uniChar[2] ;
106 uniChar[0] = 0;
107 uniChar[1] = 0;
108
109 UInt32 keyCode ;
110 UInt32 modifiers ;
111 Point point ;
112 UInt32 when = EventTimeToTicks( GetEventTime( event ) ) ;
113
114 #if wxUSE_UNICODE
115 ByteCount dataSize = 0 ;
116 if ( GetEventParameter(event, kEventParamKeyUnicodes, typeUnicodeText,
117 NULL, 0 , &dataSize, NULL ) == noErr)
118 {
119 UniChar buf[2] ;
120 int numChars = dataSize / sizeof( UniChar) + 1;
121
122 UniChar* charBuf = buf ;
123
124 if ( numChars * 2 > 4 )
125 charBuf = new UniChar[ numChars ] ;
126 GetEventParameter(event, kEventParamKeyUnicodes, typeUnicodeText, NULL,
127 dataSize , NULL , charBuf) ;
128 charBuf[ numChars - 1 ] = 0;
129
130 #if SIZEOF_WCHAR_T == 2
131 uniChar = charBuf[0] ;
132 #else
133 wxMBConvUTF16 converter ;
134 converter.MB2WC( uniChar , (const char*)charBuf , 2 ) ;
135 #endif
136
137 if ( numChars * 2 > 4 )
138 delete[] charBuf ;
139 }
140 #endif
141
142 GetEventParameter(event, kEventParamKeyMacCharCodes, typeChar, NULL,
143 sizeof(char), NULL, &charCode );
144 GetEventParameter(event, kEventParamKeyCode, typeUInt32, NULL,
145 sizeof(UInt32), NULL, &keyCode );
146 GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, NULL,
147 sizeof(UInt32), NULL, &modifiers );
148 GetEventParameter(event, kEventParamMouseLocation, typeQDPoint, NULL,
149 sizeof(Point), NULL, &point );
150
151 UInt32 message = (keyCode << 8) + charCode;
152 switch ( GetEventKind( event ) )
153 {
154 case kEventRawKeyRepeat :
155 case kEventRawKeyDown :
156 {
157 WXEVENTREF formerEvent = wxTheApp->MacGetCurrentEvent() ;
158 WXEVENTHANDLERCALLREF formerHandler =
159 wxTheApp->MacGetCurrentEventHandlerCallRef() ;
160
161 wxTheApp->MacSetCurrentEvent( event , handler ) ;
162 if ( /* focus && */ wxTheApp->MacSendKeyDownEvent(
163 focus, message, modifiers, when, point.h, point.v, uniChar[0]))
164 {
165 result = noErr ;
166 }
167 wxTheApp->MacSetCurrentEvent( formerEvent , formerHandler ) ;
168 }
169 break ;
170
171 case kEventRawKeyUp :
172 if ( /* focus && */ wxTheApp->MacSendKeyUpEvent(
173 focus , message , modifiers , when , point.h , point.v , uniChar[0] ) )
174 {
175 result = noErr ;
176 }
177 break ;
178
179 case kEventRawKeyModifiersChanged :
180 {
181 wxKeyEvent event(wxEVT_KEY_DOWN);
182
183 event.m_shiftDown = modifiers & shiftKey;
184 event.m_controlDown = modifiers & controlKey;
185 event.m_altDown = modifiers & optionKey;
186 event.m_metaDown = modifiers & cmdKey;
187 event.m_x = point.h;
188 event.m_y = point.v;
189
190 #if wxUSE_UNICODE
191 event.m_uniChar = uniChar[0] ;
192 #endif
193
194 event.SetTimestamp(when);
195 event.SetEventObject(focus);
196
197 if ( /* focus && */ (modifiers ^ wxApp::s_lastModifiers ) & controlKey )
198 {
199 event.m_keyCode = WXK_CONTROL ;
200 event.SetEventType( ( modifiers & controlKey ) ? wxEVT_KEY_DOWN : wxEVT_KEY_UP ) ;
201 focus->GetEventHandler()->ProcessEvent( event ) ;
202 }
203 if ( /* focus && */ (modifiers ^ wxApp::s_lastModifiers ) & shiftKey )
204 {
205 event.m_keyCode = WXK_SHIFT ;
206 event.SetEventType( ( modifiers & shiftKey ) ? wxEVT_KEY_DOWN : wxEVT_KEY_UP ) ;
207 focus->GetEventHandler()->ProcessEvent( event ) ;
208 }
209 if ( /* focus && */ (modifiers ^ wxApp::s_lastModifiers ) & optionKey )
210 {
211 event.m_keyCode = WXK_ALT ;
212 event.SetEventType( ( modifiers & optionKey ) ? wxEVT_KEY_DOWN : wxEVT_KEY_UP ) ;
213 focus->GetEventHandler()->ProcessEvent( event ) ;
214 }
215 if ( /* focus && */ (modifiers ^ wxApp::s_lastModifiers ) & cmdKey )
216 {
217 event.m_keyCode = WXK_COMMAND ;
218 event.SetEventType( ( modifiers & cmdKey ) ? wxEVT_KEY_DOWN : wxEVT_KEY_UP ) ;
219 focus->GetEventHandler()->ProcessEvent( event ) ;
220 }
221
222 wxApp::s_lastModifiers = modifiers ;
223 }
224 break ;
225
226 default:
227 break;
228 }
229
230 return result ;
231 }
232
233 static pascal OSStatus wxWebViewWebKitEventHandler( EventHandlerCallRef handler , EventRef event , void *data )
234 {
235 OSStatus result = eventNotHandledErr ;
236
237 wxMacCarbonEvent cEvent( event ) ;
238
239 ControlRef controlRef ;
240 wxWebViewWebKit* thisWindow = (wxWebViewWebKit*) data ;
241 wxNonOwnedWindow* tlw = NULL;
242 if (thisWindow)
243 tlw = thisWindow->MacGetTopLevelWindow();
244
245 cEvent.GetParameter( kEventParamDirectObject , &controlRef ) ;
246
247 wxWindow* currentMouseWindow = thisWindow ;
248
249 if ( wxApp::s_captureWindow )
250 currentMouseWindow = wxApp::s_captureWindow;
251
252 switch ( GetEventClass( event ) )
253 {
254 case kEventClassKeyboard:
255 {
256 result = wxWebKitKeyEventHandler(handler, event, data);
257 break;
258 }
259
260 case kEventClassTextInput:
261 {
262 result = wxMacUnicodeTextEventHandler(handler, event, data);
263 break;
264 }
265
266 case kEventClassMouse:
267 {
268 switch ( GetEventKind( event ) )
269 {
270 case kEventMouseDragged :
271 case kEventMouseMoved :
272 case kEventMouseDown :
273 case kEventMouseUp :
274 {
275 wxMouseEvent wxevent(wxEVT_LEFT_DOWN);
276 SetupMouseEvent( wxevent , cEvent ) ;
277
278 currentMouseWindow->ScreenToClient( &wxevent.m_x , &wxevent.m_y ) ;
279 wxevent.SetEventObject( currentMouseWindow ) ;
280 wxevent.SetId( currentMouseWindow->GetId() ) ;
281
282 if ( currentMouseWindow->GetEventHandler()->ProcessEvent(wxevent) )
283 {
284 result = noErr;
285 }
286
287 break; // this should enable WebKit to fire mouse dragged and mouse up events...
288 }
289 default :
290 break ;
291 }
292 }
293 default:
294 break;
295 }
296
297 result = CallNextEventHandler(handler, event);
298 return result ;
299 }
300
301 DEFINE_ONE_SHOT_HANDLER_GETTER( wxWebViewWebKitEventHandler )
302
303 #endif
304
305 //---------------------------------------------------------
306 // helper functions for NSString<->wxString conversion
307 //---------------------------------------------------------
308
309 inline wxString wxStringWithNSString(NSString *nsstring)
310 {
311 #if wxUSE_UNICODE
312 return wxString([nsstring UTF8String], wxConvUTF8);
313 #else
314 return wxString([nsstring lossyCString]);
315 #endif // wxUSE_UNICODE
316 }
317
318 inline NSString* wxNSStringWithWxString(const wxString &wxstring)
319 {
320 #if wxUSE_UNICODE
321 return [NSString stringWithUTF8String: wxstring.mb_str(wxConvUTF8)];
322 #else
323 return [NSString stringWithCString: wxstring.c_str() length:wxstring.Len()];
324 #endif // wxUSE_UNICODE
325 }
326
327 @interface MyFrameLoadMonitor : NSObject
328 {
329 wxWebViewWebKit* webKitWindow;
330 }
331
332 - initWithWxWindow: (wxWebViewWebKit*)inWindow;
333
334 @end
335
336 @interface MyPolicyDelegate : NSObject
337 {
338 wxWebViewWebKit* webKitWindow;
339 }
340
341 - initWithWxWindow: (wxWebViewWebKit*)inWindow;
342
343 @end
344
345 // ----------------------------------------------------------------------------
346 // creation/destruction
347 // ----------------------------------------------------------------------------
348
349 bool wxWebViewWebKit::Create(wxWindow *parent,
350 wxWindowID winID,
351 const wxString& strURL,
352 const wxPoint& pos,
353 const wxSize& size, long style,
354 const wxString& name)
355 {
356 m_busy = false;
357 //m_pageTitle = _("Untitled Page");
358
359 //still needed for wxCocoa??
360 /*
361 int width, height;
362 wxSize sizeInstance;
363 if (size.x == wxDefaultCoord || size.y == wxDefaultCoord)
364 {
365 m_parent->GetClientSize(&width, &height);
366 sizeInstance.x = width;
367 sizeInstance.y = height;
368 }
369 else
370 {
371 sizeInstance.x = size.x;
372 sizeInstance.y = size.y;
373 }
374 */
375 // now create and attach WebKit view...
376 #ifdef __WXCOCOA__
377 wxControl::Create(parent, m_windowID, pos, sizeInstance, style, name);
378 SetSize(pos.x, pos.y, sizeInstance.x, sizeInstance.y);
379
380 wxTopLevelWindowCocoa *topWin = wxDynamicCast(this, wxTopLevelWindowCocoa);
381 NSWindow* nsWin = topWin->GetNSWindow();
382 NSRect rect;
383 rect.origin.x = pos.x;
384 rect.origin.y = pos.y;
385 rect.size.width = sizeInstance.x;
386 rect.size.height = sizeInstance.y;
387 m_webView = (WebView*)[[WebView alloc] initWithFrame:rect
388 frameName:@"webkitFrame"
389 groupName:@"webkitGroup"];
390 SetNSView(m_webView);
391 [m_cocoaNSView release];
392
393 if(m_parent) m_parent->CocoaAddChild(this);
394 SetInitialFrameRect(pos,sizeInstance);
395 #else
396 wxControl::Create(parent, winID, pos, size, style, wxDefaultValidator, name);
397
398 #if wxOSX_USE_CARBON
399 m_peer = new wxMacControl(this);
400 WebInitForCarbon();
401 HIWebViewCreate( m_peer->GetControlRefAddr() );
402
403 m_webView = (WebView*) HIWebViewGetWebView( m_peer->GetControlRef() );
404
405 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_3
406 if ( UMAGetSystemVersion() >= 0x1030 )
407 HIViewChangeFeatures( m_peer->GetControlRef() , kHIViewIsOpaque , 0 ) ;
408 #endif
409 InstallControlEventHandler(m_peer->GetControlRef(),
410 GetwxWebViewWebKitEventHandlerUPP(),
411 GetEventTypeCount(eventList), eventList, this,
412 (EventHandlerRef *)&m_webKitCtrlEventHandler);
413 #else
414 NSRect r = wxOSXGetFrameForControl( this, pos , size ) ;
415 m_webView = [[WebView alloc] initWithFrame:r
416 frameName:@"webkitFrame"
417 groupName:@"webkitGroup"];
418 m_peer = new wxWidgetCocoaImpl( this, m_webView );
419 #endif
420
421 wx_webviewctrls[m_webView] = this;
422
423 MacPostControlCreate(pos, size);
424
425 #if wxOSX_USE_CARBON
426 HIViewSetVisible( m_peer->GetControlRef(), true );
427 #endif
428 [m_webView setHidden:false];
429
430 #endif
431
432 // Register event listener interfaces
433 MyFrameLoadMonitor* myFrameLoadMonitor =
434 [[MyFrameLoadMonitor alloc] initWithWxWindow: this];
435
436 [m_webView setFrameLoadDelegate:myFrameLoadMonitor];
437
438 // this is used to veto page loads, etc.
439 MyPolicyDelegate* myPolicyDelegate =
440 [[MyPolicyDelegate alloc] initWithWxWindow: this];
441
442 [m_webView setPolicyDelegate:myPolicyDelegate];
443
444 InternalLoadURL(strURL);
445 return true;
446 }
447
448 wxWebViewWebKit::~wxWebViewWebKit()
449 {
450 MyFrameLoadMonitor* myFrameLoadMonitor = [m_webView frameLoadDelegate];
451 MyPolicyDelegate* myPolicyDelegate = [m_webView policyDelegate];
452 [m_webView setFrameLoadDelegate: nil];
453 [m_webView setPolicyDelegate: nil];
454
455 if (myFrameLoadMonitor)
456 [myFrameLoadMonitor release];
457
458 if (myPolicyDelegate)
459 [myPolicyDelegate release];
460 }
461
462 // ----------------------------------------------------------------------------
463 // public methods
464 // ----------------------------------------------------------------------------
465
466 void wxWebViewWebKit::InternalLoadURL(const wxString &url)
467 {
468 if( !m_webView )
469 return;
470
471 [[m_webView mainFrame] loadRequest:[NSURLRequest requestWithURL:
472 [NSURL URLWithString:wxNSStringWithWxString(url)]]];
473 }
474
475 bool wxWebViewWebKit::CanGoBack()
476 {
477 if ( !m_webView )
478 return false;
479
480 return [m_webView canGoBack];
481 }
482
483 bool wxWebViewWebKit::CanGoForward()
484 {
485 if ( !m_webView )
486 return false;
487
488 return [m_webView canGoForward];
489 }
490
491 void wxWebViewWebKit::GoBack()
492 {
493 if ( !m_webView )
494 return;
495
496 bool result = [(WebView*)m_webView goBack];
497
498 // TODO: return result (if it also exists in other backends...)
499 //return result;
500 }
501
502 void wxWebViewWebKit::GoForward()
503 {
504 if ( !m_webView )
505 return;
506
507 bool result = [(WebView*)m_webView goForward];
508
509 // TODO: return result (if it also exists in other backends...)
510 //return result;
511 }
512
513 void wxWebViewWebKit::Reload(wxWebViewReloadFlags flags)
514 {
515 if ( !m_webView )
516 return;
517
518 if (flags & wxWEB_VIEW_RELOAD_NO_CACHE)
519 {
520 // TODO: test this indeed bypasses the cache
521 [[m_webView preferences] setUsesPageCache:NO];
522 [[m_webView mainFrame] reload];
523 [[m_webView preferences] setUsesPageCache:YES];
524 }
525 else
526 {
527 [[m_webView mainFrame] reload];
528 }
529 }
530
531 void wxWebViewWebKit::Stop()
532 {
533 if ( !m_webView )
534 return;
535
536 [[m_webView mainFrame] stopLoading];
537 }
538
539 bool wxWebViewWebKit::CanGetPageSource()
540 {
541 if ( !m_webView )
542 return false;
543
544 WebDataSource* dataSource = [[m_webView mainFrame] dataSource];
545 return ( [[dataSource representation] canProvideDocumentSource] );
546 }
547
548 wxString wxWebViewWebKit::GetPageSource()
549 {
550
551 if (CanGetPageSource())
552 {
553 WebDataSource* dataSource = [[m_webView mainFrame] dataSource];
554 wxASSERT (dataSource != nil);
555
556 id<WebDocumentRepresentation> representation = [dataSource representation];
557 wxASSERT (representation != nil);
558
559 NSString* source = [representation documentSource];
560 if (source == nil)
561 {
562 return wxEmptyString;
563 }
564
565 return wxStringWithNSString( source );
566 }
567
568 return wxEmptyString;
569 }
570
571 bool wxWebViewWebKit::CanIncreaseTextSize()
572 {
573 if ( !m_webView )
574 return false;
575
576 if ([m_webView canMakeTextLarger])
577 return true;
578 else
579 return false;
580 }
581
582 void wxWebViewWebKit::IncreaseTextSize()
583 {
584 if ( !m_webView )
585 return;
586
587 if (CanIncreaseTextSize())
588 [m_webView makeTextLarger:(WebView*)m_webView];
589 }
590
591 bool wxWebViewWebKit::CanDecreaseTextSize()
592 {
593 if ( !m_webView )
594 return false;
595
596 if ([m_webView canMakeTextSmaller])
597 return true;
598 else
599 return false;
600 }
601
602 void wxWebViewWebKit::DecreaseTextSize()
603 {
604 if ( !m_webView )
605 return;
606
607 if (CanDecreaseTextSize())
608 [m_webView makeTextSmaller:(WebView*)m_webView];
609 }
610
611 void wxWebViewWebKit::Print()
612 {
613
614 // TODO: allow specifying the "show prompt" parameter in Print() ?
615 bool showPrompt = true;
616
617 if ( !m_webView )
618 return;
619
620 id view = [[[m_webView mainFrame] frameView] documentView];
621 NSPrintOperation *op = [NSPrintOperation printOperationWithView:view
622 printInfo: [NSPrintInfo sharedPrintInfo]];
623 if (showPrompt)
624 {
625 [op setShowsPrintPanel: showPrompt];
626 // in my tests, the progress bar always freezes and it stops the whole
627 // print operation. do not turn this to true unless there is a
628 // workaround for the bug.
629 [op setShowsProgressPanel: false];
630 }
631 // Print it.
632 [op runOperation];
633 }
634
635 void wxWebViewWebKit::SetEditable(bool enable)
636 {
637 if ( !m_webView )
638 return;
639
640 [m_webView setEditable:enable ];
641 }
642
643 bool wxWebViewWebKit::IsEditable()
644 {
645 if ( !m_webView )
646 return false;
647
648 return [m_webView isEditable];
649 }
650
651 void wxWebViewWebKit::SetZoomType(wxWebViewZoomType zoomType)
652 {
653 // there is only one supported zoom type at the moment so this setter
654 // does nothing beyond checking sanity
655 wxASSERT(zoomType == wxWEB_VIEW_ZOOM_TYPE_TEXT);
656 }
657
658 wxWebViewZoomType wxWebViewWebKit::GetZoomType() const
659 {
660 // for now that's the only one that is supported
661 // FIXME: does the default zoom type change depending on webkit versions? :S
662 // Then this will be wrong
663 return wxWEB_VIEW_ZOOM_TYPE_TEXT;
664 }
665
666 bool wxWebViewWebKit::CanSetZoomType(wxWebViewZoomType type) const
667 {
668 switch (type)
669 {
670 // for now that's the only one that is supported
671 // TODO: I know recent versions of webkit support layout zoom too,
672 // check if we can support it
673 case wxWEB_VIEW_ZOOM_TYPE_TEXT:
674 return true;
675
676 default:
677 return false;
678 }
679 }
680
681 int wxWebViewWebKit::GetScrollPos()
682 {
683 id result = [[m_webView windowScriptObject]
684 evaluateWebScript:@"document.body.scrollTop"];
685 return [result intValue];
686 }
687
688 void wxWebViewWebKit::SetScrollPos(int pos)
689 {
690 if ( !m_webView )
691 return;
692
693 wxString javascript;
694 javascript.Printf(wxT("document.body.scrollTop = %d;"), pos);
695 [[m_webView windowScriptObject] evaluateWebScript:
696 (NSString*)wxNSStringWithWxString( javascript )];
697 }
698
699 wxString wxWebViewWebKit::GetSelectedText()
700 {
701 NSString* selection = [[m_webView selectedDOMRange] markupString];
702 if (!selection) return wxEmptyString;
703
704 return wxStringWithNSString(selection);
705 }
706
707 void wxWebViewWebKit::RunScript(const wxString& javascript)
708 {
709 if ( !m_webView )
710 return;
711
712 [[m_webView windowScriptObject] evaluateWebScript:
713 (NSString*)wxNSStringWithWxString( javascript )];
714 }
715
716 void wxWebViewWebKit::OnSize(wxSizeEvent &event)
717 {
718 #if defined(__WXMAC_) && wxOSX_USE_CARBON
719 // This is a nasty hack because WebKit seems to lose its position when it is
720 // embedded in a control that is not itself the content view for a TLW.
721 // I put it in OnSize because these calcs are not perfect, and in fact are
722 // basically guesses based on reverse engineering, so it's best to give
723 // people the option of overriding OnSize with their own calcs if need be.
724 // I also left some test debugging print statements as a convenience if
725 // a(nother) problem crops up.
726
727 wxWindow* tlw = MacGetTopLevelWindow();
728
729 NSRect frame = [(WebView*)m_webView frame];
730 NSRect bounds = [(WebView*)m_webView bounds];
731
732 #if DEBUG_WEBKIT_SIZING
733 fprintf(stderr,"Carbon window x=%d, y=%d, width=%d, height=%d\n",
734 GetPosition().x, GetPosition().y, GetSize().x, GetSize().y);
735 fprintf(stderr, "Cocoa window frame x=%G, y=%G, width=%G, height=%G\n",
736 frame.origin.x, frame.origin.y,
737 frame.size.width, frame.size.height);
738 fprintf(stderr, "Cocoa window bounds x=%G, y=%G, width=%G, height=%G\n",
739 bounds.origin.x, bounds.origin.y,
740 bounds.size.width, bounds.size.height);
741 #endif
742
743 // This must be the case that Apple tested with, because well, in this one case
744 // we don't need to do anything! It just works. ;)
745 if (GetParent() == tlw) return;
746
747 // since we no longer use parent coordinates, we always want 0,0.
748 int x = 0;
749 int y = 0;
750
751 HIRect rect;
752 rect.origin.x = x;
753 rect.origin.y = y;
754
755 #if DEBUG_WEBKIT_SIZING
756 printf("Before conversion, origin is: x = %d, y = %d\n", x, y);
757 #endif
758
759 // NB: In most cases, when calling HIViewConvertRect, what people want is to
760 // use GetRootControl(), and this tripped me up at first. But in fact, what
761 // we want is the root view, because we need to make the y origin relative
762 // to the very top of the window, not its contents, since we later flip
763 // the y coordinate for Cocoa.
764 HIViewConvertRect (&rect, m_peer->GetControlRef(),
765 HIViewGetRoot(
766 (WindowRef) MacGetTopLevelWindowRef()
767 ));
768
769 x = (int)rect.origin.x;
770 y = (int)rect.origin.y;
771
772 #if DEBUG_WEBKIT_SIZING
773 printf("Moving Cocoa frame origin to: x = %d, y = %d\n", x, y);
774 #endif
775
776 if (tlw){
777 //flip the y coordinate to convert to Cocoa coordinates
778 y = tlw->GetSize().y - ((GetSize().y) + y);
779 }
780
781 #if DEBUG_WEBKIT_SIZING
782 printf("y = %d after flipping value\n", y);
783 #endif
784
785 frame.origin.x = x;
786 frame.origin.y = y;
787 [(WebView*)m_webView setFrame:frame];
788
789 if (IsShown())
790 [(WebView*)m_webView display];
791 event.Skip();
792 #endif
793 }
794
795 void wxWebViewWebKit::MacVisibilityChanged(){
796 #if defined(__WXMAC__) && wxOSX_USE_CARBON
797 bool isHidden = !IsControlVisible( m_peer->GetControlRef());
798 if (!isHidden)
799 [(WebView*)m_webView display];
800
801 [m_webView setHidden:isHidden];
802 #endif
803 }
804
805 void wxWebViewWebKit::LoadUrl(const wxString& url)
806 {
807 InternalLoadURL(url);
808 }
809
810 wxString wxWebViewWebKit::GetCurrentURL()
811 {
812 return wxStringWithNSString([m_webView mainFrameURL]);
813 }
814
815 wxString wxWebViewWebKit::GetCurrentTitle()
816 {
817 return GetPageTitle();
818 }
819
820 float wxWebViewWebKit::GetWebkitZoom()
821 {
822 return [m_webView textSizeMultiplier];
823 }
824
825 void wxWebViewWebKit::SetWebkitZoom(float zoom)
826 {
827 [m_webView setTextSizeMultiplier:zoom];
828 }
829
830 wxWebViewZoom wxWebViewWebKit::GetZoom()
831 {
832 float zoom = GetWebkitZoom();
833
834 // arbitrary way to map float zoom to our common zoom enum
835 if (zoom <= 0.55)
836 {
837 return wxWEB_VIEW_ZOOM_TINY;
838 }
839 else if (zoom > 0.55 && zoom <= 0.85)
840 {
841 return wxWEB_VIEW_ZOOM_SMALL;
842 }
843 else if (zoom > 0.85 && zoom <= 1.15)
844 {
845 return wxWEB_VIEW_ZOOM_MEDIUM;
846 }
847 else if (zoom > 1.15 && zoom <= 1.45)
848 {
849 return wxWEB_VIEW_ZOOM_LARGE;
850 }
851 else if (zoom > 1.45)
852 {
853 return wxWEB_VIEW_ZOOM_LARGEST;
854 }
855
856 // to shut up compilers, this can never be reached logically
857 wxASSERT(false);
858 return wxWEB_VIEW_ZOOM_MEDIUM;
859 }
860
861 void wxWebViewWebKit::SetZoom(wxWebViewZoom zoom)
862 {
863 // arbitrary way to map our common zoom enum to float zoom
864 switch (zoom)
865 {
866 case wxWEB_VIEW_ZOOM_TINY:
867 SetWebkitZoom(0.4f);
868 break;
869
870 case wxWEB_VIEW_ZOOM_SMALL:
871 SetWebkitZoom(0.7f);
872 break;
873
874 case wxWEB_VIEW_ZOOM_MEDIUM:
875 SetWebkitZoom(1.0f);
876 break;
877
878 case wxWEB_VIEW_ZOOM_LARGE:
879 SetWebkitZoom(1.3);
880 break;
881
882 case wxWEB_VIEW_ZOOM_LARGEST:
883 SetWebkitZoom(1.6);
884 break;
885
886 default:
887 wxASSERT(false);
888 }
889
890 }
891
892 void wxWebViewWebKit::SetPage(const wxString& src, const wxString& baseUrl)
893 {
894 if ( !m_webView )
895 return;
896
897 [[m_webView mainFrame] loadHTMLString:(NSString*)wxNSStringWithWxString(src)
898 baseURL:[NSURL URLWithString:
899 wxNSStringWithWxString( baseUrl )]];
900 }
901
902 void wxWebViewWebKit::Cut()
903 {
904 if ( !m_webView )
905 return;
906
907 [(WebView*)m_webView cut:m_webView];
908 }
909
910 void wxWebViewWebKit::Copy()
911 {
912 if ( !m_webView )
913 return;
914
915 [(WebView*)m_webView copy:m_webView];
916 }
917
918 void wxWebViewWebKit::Paste()
919 {
920 if ( !m_webView )
921 return;
922
923 [(WebView*)m_webView paste:m_webView];
924 }
925
926 void wxWebViewWebKit::DeleteSelection()
927 {
928 if ( !m_webView )
929 return;
930
931 [(WebView*)m_webView deleteSelection];
932 }
933
934 bool wxWebViewWebKit::HasSelection()
935 {
936 DOMRange* range = [m_webView selectedDOMRange];
937 if(!range)
938 {
939 return false;
940 }
941 else
942 {
943 return true;
944 }
945 }
946
947 void wxWebViewWebKit::EnableHistory(bool enable)
948 {
949 if ( !m_webView )
950 return;
951
952 [m_webView setMaintainsBackForwardList:enable];
953 }
954
955 void wxWebViewWebKit::ClearHistory()
956 {
957 [m_webView setMaintainsBackForwardList:NO];
958 [m_webView setMaintainsBackForwardList:YES];
959 }
960
961 wxVector<wxSharedPtr<wxWebHistoryItem> > wxWebViewWebKit::GetBackwardHistory()
962 {
963 wxVector<wxSharedPtr<wxWebHistoryItem> > backhist;
964 WebBackForwardList* history = [m_webView backForwardList];
965 int count = [history backListCount];
966 for(int i = -count; i < 0; i++)
967 {
968 WebHistoryItem* item = [history itemAtIndex:i];
969 wxString url = wxStringWithNSString([item URLString]);
970 wxString title = wxStringWithNSString([item title]);
971 wxWebHistoryItem* wxitem = new wxWebHistoryItem(url, title);
972 wxitem->m_histItem = item;
973 wxSharedPtr<wxWebHistoryItem> itemptr(wxitem);
974 backhist.push_back(itemptr);
975 }
976 return backhist;
977 }
978
979 wxVector<wxSharedPtr<wxWebHistoryItem> > wxWebViewWebKit::GetForwardHistory()
980 {
981 wxVector<wxSharedPtr<wxWebHistoryItem> > forwardhist;
982 WebBackForwardList* history = [m_webView backForwardList];
983 int count = [history forwardListCount];
984 for(int i = 1; i <= count; i++)
985 {
986 WebHistoryItem* item = [history itemAtIndex:i];
987 wxString url = wxStringWithNSString([item URLString]);
988 wxString title = wxStringWithNSString([item title]);
989 wxWebHistoryItem* wxitem = new wxWebHistoryItem(url, title);
990 wxitem->m_histItem = item;
991 wxSharedPtr<wxWebHistoryItem> itemptr(wxitem);
992 forwardhist.push_back(itemptr);
993 }
994 return forwardhist;
995 }
996
997 void wxWebViewWebKit::LoadHistoryItem(wxSharedPtr<wxWebHistoryItem> item)
998 {
999 [m_webView goToBackForwardItem:item->m_histItem];
1000 }
1001
1002 bool wxWebViewWebKit::CanUndo()
1003 {
1004 return [[m_webView undoManager] canUndo];
1005 }
1006
1007 bool wxWebViewWebKit::CanRedo()
1008 {
1009 return [[m_webView undoManager] canRedo];
1010 }
1011
1012 void wxWebViewWebKit::Undo()
1013 {
1014 [[m_webView undoManager] undo];
1015 }
1016
1017 void wxWebViewWebKit::Redo()
1018 {
1019 [[m_webView undoManager] redo];
1020 }
1021
1022 //------------------------------------------------------------
1023 // Listener interfaces
1024 //------------------------------------------------------------
1025
1026 // NB: I'm still tracking this down, but it appears the Cocoa window
1027 // still has these events fired on it while the Carbon control is being
1028 // destroyed. Therefore, we must be careful to check both the existence
1029 // of the Carbon control and the event handler before firing events.
1030
1031 @implementation MyFrameLoadMonitor
1032
1033 - initWithWxWindow: (wxWebViewWebKit*)inWindow
1034 {
1035 [super init];
1036 webKitWindow = inWindow; // non retained
1037 return self;
1038 }
1039
1040 - (void)webView:(WebView *)sender
1041 didStartProvisionalLoadForFrame:(WebFrame *)frame
1042 {
1043 wxASSERT(wx_webviewctrls.find(sender) != wx_webviewctrls.end());
1044 wx_webviewctrls[sender]->m_busy = true;
1045 }
1046
1047 - (void)webView:(WebView *)sender didCommitLoadForFrame:(WebFrame *)frame
1048 {
1049 wxASSERT(wx_webviewctrls.find(sender) != wx_webviewctrls.end());
1050 wx_webviewctrls[sender]->m_busy = true;
1051
1052 if (webKitWindow && frame == [sender mainFrame]){
1053 NSString *url = [[[[frame dataSource] request] URL] absoluteString];
1054 wxString target = wxStringWithNSString([frame name]);
1055 wxWebNavigationEvent thisEvent(wxEVT_COMMAND_WEB_VIEW_NAVIGATED,
1056 wx_webviewctrls[sender]->GetId(),
1057 wxStringWithNSString( url ),
1058 target, false);
1059
1060 if (webKitWindow && webKitWindow->GetEventHandler())
1061 webKitWindow->GetEventHandler()->ProcessEvent(thisEvent);
1062 }
1063 }
1064
1065 - (void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame
1066 {
1067 wxASSERT(wx_webviewctrls.find(sender) != wx_webviewctrls.end());
1068 wx_webviewctrls[sender]->m_busy = false;
1069
1070 if (webKitWindow && frame == [sender mainFrame]){
1071 NSString *url = [[[[frame dataSource] request] URL] absoluteString];
1072
1073 wxString target = wxStringWithNSString([frame name]);
1074 wxWebNavigationEvent thisEvent(wxEVT_COMMAND_WEB_VIEW_LOADED,
1075 wx_webviewctrls[sender]->GetId(),
1076 wxStringWithNSString( url ),
1077 target, false);
1078
1079 if (webKitWindow && webKitWindow->GetEventHandler())
1080 webKitWindow->GetEventHandler()->ProcessEvent(thisEvent);
1081 }
1082 }
1083
1084 wxString nsErrorToWxHtmlError(NSError* error, wxWebNavigationError* out)
1085 {
1086 *out = wxWEB_NAV_ERR_OTHER;
1087
1088 if ([[error domain] isEqualToString:NSURLErrorDomain])
1089 {
1090 switch ([error code])
1091 {
1092 case NSURLErrorCannotFindHost:
1093 case NSURLErrorFileDoesNotExist:
1094 case NSURLErrorRedirectToNonExistentLocation:
1095 *out = wxWEB_NAV_ERR_NOT_FOUND;
1096 break;
1097
1098 case NSURLErrorResourceUnavailable:
1099 case NSURLErrorHTTPTooManyRedirects:
1100 case NSURLErrorDataLengthExceedsMaximum:
1101 case NSURLErrorBadURL:
1102 case NSURLErrorFileIsDirectory:
1103 *out = wxWEB_NAV_ERR_REQUEST;
1104 break;
1105
1106 case NSURLErrorTimedOut:
1107 case NSURLErrorDNSLookupFailed:
1108 case NSURLErrorNetworkConnectionLost:
1109 case NSURLErrorCannotConnectToHost:
1110 case NSURLErrorNotConnectedToInternet:
1111 //case NSURLErrorInternationalRoamingOff:
1112 //case NSURLErrorCallIsActive:
1113 //case NSURLErrorDataNotAllowed:
1114 *out = wxWEB_NAV_ERR_CONNECTION;
1115 break;
1116
1117 case NSURLErrorCancelled:
1118 case NSURLErrorUserCancelledAuthentication:
1119 *out = wxWEB_NAV_ERR_USER_CANCELLED;
1120 break;
1121
1122 case NSURLErrorCannotDecodeRawData:
1123 case NSURLErrorCannotDecodeContentData:
1124 case NSURLErrorBadServerResponse:
1125 case NSURLErrorCannotParseResponse:
1126 *out = wxWEB_NAV_ERR_REQUEST;
1127 break;
1128
1129 case NSURLErrorUserAuthenticationRequired:
1130 case NSURLErrorSecureConnectionFailed:
1131 case NSURLErrorClientCertificateRequired:
1132 *out = wxWEB_NAV_ERR_AUTH;
1133 break;
1134
1135 case NSURLErrorNoPermissionsToReadFile:
1136 *out = wxWEB_NAV_ERR_SECURITY;
1137 break;
1138
1139 case NSURLErrorServerCertificateHasBadDate:
1140 case NSURLErrorServerCertificateUntrusted:
1141 case NSURLErrorServerCertificateHasUnknownRoot:
1142 case NSURLErrorServerCertificateNotYetValid:
1143 case NSURLErrorClientCertificateRejected:
1144 *out = wxWEB_NAV_ERR_CERTIFICATE;
1145 break;
1146 }
1147 }
1148
1149 wxString message = wxStringWithNSString([error localizedDescription]);
1150 NSString* detail = [error localizedFailureReason];
1151 if (detail != NULL)
1152 {
1153 message = message + " (" + wxStringWithNSString(detail) + ")";
1154 }
1155 return message;
1156 }
1157
1158 - (void)webView:(WebView *)sender didFailLoadWithError:(NSError*) error
1159 forFrame:(WebFrame *)frame
1160 {
1161 wxASSERT(wx_webviewctrls.find(sender) != wx_webviewctrls.end());
1162 wx_webviewctrls[sender]->m_busy = false;
1163
1164 if (webKitWindow && frame == [sender mainFrame]){
1165 NSString *url = [[[[frame dataSource] request] URL] absoluteString];
1166
1167 wxWebNavigationError type;
1168 wxString description = nsErrorToWxHtmlError(error, &type);
1169 wxWebNavigationEvent thisEvent(wxEVT_COMMAND_WEB_VIEW_ERROR,
1170 wx_webviewctrls[sender]->GetId(),
1171 wxStringWithNSString( url ),
1172 wxEmptyString, false);
1173 thisEvent.SetString(description);
1174 thisEvent.SetInt(type);
1175
1176 if (webKitWindow && webKitWindow->GetEventHandler())
1177 {
1178 webKitWindow->GetEventHandler()->ProcessEvent(thisEvent);
1179 }
1180 }
1181 }
1182
1183 - (void)webView:(WebView *)sender
1184 didFailProvisionalLoadWithError:(NSError*)error
1185 forFrame:(WebFrame *)frame
1186 {
1187 wxASSERT(wx_webviewctrls.find(sender) != wx_webviewctrls.end());
1188 wx_webviewctrls[sender]->m_busy = false;
1189
1190 if (webKitWindow && frame == [sender mainFrame]){
1191 NSString *url = [[[[frame provisionalDataSource] request] URL]
1192 absoluteString];
1193
1194 wxWebNavigationError type;
1195 wxString description = nsErrorToWxHtmlError(error, &type);
1196 wxWebNavigationEvent thisEvent(wxEVT_COMMAND_WEB_VIEW_ERROR,
1197 wx_webviewctrls[sender]->GetId(),
1198 wxStringWithNSString( url ),
1199 wxEmptyString, false);
1200 thisEvent.SetString(description);
1201 thisEvent.SetInt(type);
1202
1203 if (webKitWindow && webKitWindow->GetEventHandler())
1204 webKitWindow->GetEventHandler()->ProcessEvent(thisEvent);
1205 }
1206 }
1207
1208 - (void)webView:(WebView *)sender didReceiveTitle:(NSString *)title
1209 forFrame:(WebFrame *)frame
1210 {
1211 if (webKitWindow && frame == [sender mainFrame])
1212 {
1213 webKitWindow->SetPageTitle(wxStringWithNSString( title ));
1214 }
1215 wxString target = wxStringWithNSString([frame name]);
1216 wxWebNavigationEvent thisEvent(wxEVT_COMMAND_WEB_VIEW_TITLE_CHANGED,
1217 wx_webviewctrls[sender]->GetId(),
1218 wx_webviewctrls[sender]->GetCurrentURL(),
1219 target, true);
1220
1221 thisEvent.SetString(wxStringWithNSString(title));
1222
1223 if (webKitWindow && webKitWindow->GetEventHandler())
1224 webKitWindow->GetEventHandler()->ProcessEvent(thisEvent);
1225 }
1226 @end
1227
1228 @implementation MyPolicyDelegate
1229
1230 - initWithWxWindow: (wxWebViewWebKit*)inWindow
1231 {
1232 [super init];
1233 webKitWindow = inWindow; // non retained
1234 return self;
1235 }
1236
1237 - (void)webView:(WebView *)sender
1238 decidePolicyForNavigationAction:(NSDictionary *)actionInformation
1239 request:(NSURLRequest *)request
1240 frame:(WebFrame *)frame
1241 decisionListener:(id<WebPolicyDecisionListener>)listener
1242 {
1243 wxUnusedVar(frame);
1244
1245 wxASSERT(wx_webviewctrls.find(sender) != wx_webviewctrls.end());
1246 wx_webviewctrls[sender]->m_busy = true;
1247 NSString *url = [[request URL] absoluteString];
1248 wxString target = wxStringWithNSString([frame name]);
1249 wxWebNavigationEvent thisEvent(wxEVT_COMMAND_WEB_VIEW_NAVIGATING,
1250 wx_webviewctrls[sender]->GetId(),
1251 wxStringWithNSString( url ), target, true);
1252
1253 if (webKitWindow && webKitWindow->GetEventHandler())
1254 webKitWindow->GetEventHandler()->ProcessEvent(thisEvent);
1255
1256 if (thisEvent.IsVetoed())
1257 {
1258 wx_webviewctrls[sender]->m_busy = false;
1259 [listener ignore];
1260 }
1261 else
1262 {
1263 [listener use];
1264 }
1265 }
1266
1267 - (void)webView:(WebView *)sender
1268 decidePolicyForNewWindowAction:(NSDictionary *)actionInformation
1269 request:(NSURLRequest *)request
1270 newFrameName:(NSString *)frameName
1271 decisionListener:(id < WebPolicyDecisionListener >)listener
1272 {
1273 wxUnusedVar(actionInformation);
1274
1275 wxASSERT(wx_webviewctrls.find(sender) != wx_webviewctrls.end());
1276 NSString *url = [[request URL] absoluteString];
1277 wxWebNavigationEvent thisEvent(wxEVT_COMMAND_WEB_VIEW_NEWWINDOW,
1278 wx_webviewctrls[sender]->GetId(),
1279 wxStringWithNSString( url ), "", true);
1280
1281 if (webKitWindow && webKitWindow->GetEventHandler())
1282 webKitWindow->GetEventHandler()->ProcessEvent(thisEvent);
1283
1284 [listener ignore];
1285 }
1286 @end
1287
1288 #endif //wxUSE_WEBVIEW_WEBKIT