a5fee99f848ff97413d0eeaa3598af4576b2413a
[WebKit.git] / WebKit / wx / WebView.cpp
1 /*
2  * Copyright (C) 2007 Kevin Ollivier  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 COMPUTER, INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "config.h"
27 #include "CString.h"
28 #include "Document.h"
29 #include "Element.h"
30 #include "Editor.h"
31 #include "EventHandler.h"
32 #include "FocusController.h"
33 #include "Frame.h"
34 #include "FrameLoader.h"
35 #include "FrameView.h"
36 #include "GraphicsContext.h"
37 #include "HTMLFrameOwnerElement.h"
38 #include "Logging.h"
39 #include "markup.h"
40 #include "Page.h"
41 #include "PlatformKeyboardEvent.h"
42 #include "PlatformMouseEvent.h"
43 #include "PlatformString.h"
44 #include "PlatformWheelEvent.h"
45 #include "RenderObject.h"
46 #include "RenderTreeAsText.h"
47 #include "RenderView.h"
48 #include "SelectionController.h"
49 #include "Settings.h"
50 #include "SubstituteData.h"
51
52 #include "ChromeClientWx.h"
53 #include "ContextMenuClientWx.h"
54 #include "DragClientWx.h"
55 #include "EditorClientWx.h"
56 #include "FrameLoaderClientWx.h"
57 #include "InspectorClientWx.h"
58
59 #include "ScriptController.h"
60 #include "JSDOMBinding.h"
61 #include <kjs/JSValue.h>
62 #include <kjs/ustring.h>
63
64 #include "wx/wxprec.h"
65 #ifndef WX_PRECOMP
66     #include "wx/wx.h"
67 #endif
68
69 #include "WebView.h"
70 #include "WebViewPrivate.h"
71
72 #include <wx/defs.h>
73 #include <wx/dcbuffer.h>
74
75 // Match Safari's min/max zoom sizes by default
76 #define MinimumTextSizeMultiplier       0.5f
77 #define MaximumTextSizeMultiplier       3.0f
78 #define TextSizeMultiplierRatio         1.2f
79
80
81 #if defined(_MSC_VER)
82 int rint(double val)
83 {
84     return (int)(val < 0 ? val - 0.5 : val + 0.5);
85 }
86 #endif
87
88 // ----------------------------------------------------------------------------
89 // wxWebView Events
90 // ----------------------------------------------------------------------------
91
92 IMPLEMENT_DYNAMIC_CLASS(wxWebViewLoadEvent, wxCommandEvent)
93
94 DEFINE_EVENT_TYPE(wxEVT_WEBVIEW_LOAD)
95
96 wxWebViewLoadEvent::wxWebViewLoadEvent(wxWindow* win)
97 {
98     SetEventType( wxEVT_WEBVIEW_LOAD);
99     SetEventObject( win );
100     if (win)
101         SetId(win->GetId());
102 }
103
104 IMPLEMENT_DYNAMIC_CLASS(wxWebViewBeforeLoadEvent, wxCommandEvent)
105
106 DEFINE_EVENT_TYPE(wxEVT_WEBVIEW_BEFORE_LOAD)
107
108 wxWebViewBeforeLoadEvent::wxWebViewBeforeLoadEvent(wxWindow* win)
109 {
110     m_cancelled = false;
111     SetEventType(wxEVT_WEBVIEW_BEFORE_LOAD);
112     SetEventObject(win);
113     if (win)
114         SetId(win->GetId());
115 }
116
117 IMPLEMENT_DYNAMIC_CLASS(wxWebViewNewWindowEvent, wxCommandEvent)
118
119 DEFINE_EVENT_TYPE(wxEVT_WEBVIEW_NEW_WINDOW)
120
121 wxWebViewNewWindowEvent::wxWebViewNewWindowEvent(wxWindow* win)
122 {
123     SetEventType(wxEVT_WEBVIEW_NEW_WINDOW);
124     SetEventObject(win);
125     if (win)
126         SetId(win->GetId());
127 }
128
129 IMPLEMENT_DYNAMIC_CLASS(wxWebViewRightClickEvent, wxCommandEvent)
130
131 DEFINE_EVENT_TYPE(wxEVT_WEBVIEW_RIGHT_CLICK)
132
133 wxWebViewRightClickEvent::wxWebViewRightClickEvent(wxWindow* win)
134 {
135     SetEventType(wxEVT_WEBVIEW_RIGHT_CLICK);
136     SetEventObject(win);
137     if (win)
138         SetId(win->GetId());
139 }
140
141 IMPLEMENT_DYNAMIC_CLASS(wxWebViewConsoleMessageEvent, wxCommandEvent)
142
143 DEFINE_EVENT_TYPE(wxEVT_WEBVIEW_CONSOLE_MESSAGE)
144
145 wxWebViewConsoleMessageEvent::wxWebViewConsoleMessageEvent(wxWindow* win)
146 {
147     SetEventType(wxEVT_WEBVIEW_CONSOLE_MESSAGE);
148     SetEventObject(win);
149     if (win)
150         SetId(win->GetId());
151 }
152
153 IMPLEMENT_DYNAMIC_CLASS(wxWebViewReceivedTitleEvent, wxCommandEvent)
154
155 DEFINE_EVENT_TYPE(wxEVT_WEBVIEW_RECEIVED_TITLE)
156
157 wxWebViewReceivedTitleEvent::wxWebViewReceivedTitleEvent(wxWindow* win)
158 {
159     SetEventType(wxEVT_WEBVIEW_RECEIVED_TITLE);
160     SetEventObject(win);
161     if (win)
162         SetId(win->GetId());
163 }
164
165 //---------------------------------------------------------
166 // DOM Element info data type
167 //---------------------------------------------------------
168
169 wxWebViewDOMElementInfo::wxWebViewDOMElementInfo() :
170     m_domElement(NULL),
171     m_isSelected(false),
172     m_text(wxEmptyString),
173     m_imageSrc(wxEmptyString),
174     m_link(wxEmptyString)
175 {
176 }
177
178 BEGIN_EVENT_TABLE(wxWebView, wxWindow)
179     EVT_PAINT(wxWebView::OnPaint)
180     EVT_SIZE(wxWebView::OnSize)
181     EVT_MOUSE_EVENTS(wxWebView::OnMouseEvents)
182     EVT_KEY_DOWN(wxWebView::OnKeyEvents)
183     EVT_KEY_UP(wxWebView::OnKeyEvents)
184     EVT_CHAR(wxWebView::OnKeyEvents)
185     EVT_SET_FOCUS(wxWebView::OnSetFocus)
186     EVT_KILL_FOCUS(wxWebView::OnKillFocus)
187     EVT_ACTIVATE(wxWebView::OnActivate)
188 END_EVENT_TABLE()
189
190 IMPLEMENT_DYNAMIC_CLASS(wxWebView, wxWindow)
191
192 const wxChar* wxWebViewNameStr = wxT("webView");
193
194 wxWebView::wxWebView() :
195     m_textMagnifier(1.0),
196     m_isEditable(false),
197     m_isInitialized(false),
198     m_beingDestroyed(false),
199     m_title(wxEmptyString)
200 {
201 }
202
203 wxWebView::wxWebView(wxWindow* parent, int id, const wxPoint& position, 
204                      const wxSize& size, long style, const wxString& name,
205                      WebViewFrameData* data) :
206     m_textMagnifier(1.0),
207     m_isEditable(false),
208     m_isInitialized(false),
209     m_beingDestroyed(false),
210     m_title(wxEmptyString)
211 {
212     Create(parent, id, position, size, style, name, data);
213 }
214
215 bool wxWebView::Create(wxWindow* parent, int id, const wxPoint& position, 
216                        const wxSize& size, long style, const wxString& name,
217                        WebViewFrameData* data)
218 {
219     if ( (style & wxBORDER_MASK) == 0)
220         style |= wxBORDER_NONE;
221     style |= wxHSCROLL | wxVSCROLL;
222     
223     if (!wxWindow::Create(parent, id, position, size, style, name))
224         return false;
225
226 // This is necessary because we are using SharedTimerWin.cpp on Windows,
227 // due to a problem with exceptions getting eaten when using the callback
228 // approach to timers (which wx itself uses).
229 #if __WXMSW__
230     WebCore::Page::setInstanceHandle(wxGetInstance());
231 #endif
232
233     // this helps reduce flicker on platforms like MSW
234     SetBackgroundStyle(wxBG_STYLE_CUSTOM);
235
236     m_impl = new WebViewPrivate();
237
238     WebCore::InitializeLoggingChannelsIfNecessary();    
239     WebCore::HTMLFrameOwnerElement* parentFrame = 0;
240
241     // FIXME: This cast is obviously not as safe as a dynamic
242     // cast, but this allows us to get around requiring RTTI
243     // support for the moment. This is only used for subframes
244     // in any case, which aren't currently supported.
245     wxWebView* parentWebView = static_cast<wxWebView*>(parent);
246     
247     if (data) {
248         parentFrame = data->ownerElement;
249         m_impl->page = parentWebView->m_impl->frame->page();
250     }
251     else {
252         WebCore::EditorClientWx* editorClient = new WebCore::EditorClientWx();
253         m_impl->page = new WebCore::Page(new WebCore::ChromeClientWx(this), new WebCore::ContextMenuClientWx(), editorClient, new WebCore::DragClientWx(), new WebCore::InspectorClientWx());
254         editorClient->setPage(m_impl->page);
255     }
256     
257     WebCore::FrameLoaderClientWx* loaderClient = new WebCore::FrameLoaderClientWx();
258     
259     m_impl->frame = WebCore::Frame::create(m_impl->page, parentFrame, loaderClient);
260
261     loaderClient->setFrame(m_impl->frame.get());
262     loaderClient->setWebView(this);
263     
264     m_impl->frame->init();
265         
266     // Default settings - we should have wxWebViewSettings class for this
267     // eventually
268     WebCore::Settings* settings = m_impl->page->settings();
269     settings->setLoadsImagesAutomatically(true);
270     settings->setDefaultFixedFontSize(13);
271     settings->setDefaultFontSize(16);
272     settings->setSerifFontFamily("Times New Roman");
273     settings->setFixedFontFamily("Courier New");
274     settings->setSansSerifFontFamily("Arial");
275     settings->setStandardFontFamily("Times New Roman");
276     settings->setJavaScriptEnabled(true);
277
278     m_isInitialized = true;
279
280     return true;
281 }
282
283 wxWebView::~wxWebView()
284 {
285     m_beingDestroyed = true;
286     
287     m_impl->frame->loader()->detachFromParent();
288     
289     delete m_impl->page;
290     m_impl->page = 0;   
291 }
292
293 void wxWebView::Stop()
294 {
295     if (m_impl->frame && m_impl->frame->loader())
296         m_impl->frame->loader()->stop();
297 }
298
299 void wxWebView::Reload()
300 {
301     if (m_impl->frame && m_impl->frame->loader())
302         m_impl->frame->loader()->reload();
303 }
304
305 wxString wxWebView::GetPageSource()
306 {
307     if (m_impl->frame) {
308         if (m_impl->frame->view() && m_impl->frame->view()->layoutPending())
309             m_impl->frame->view()->layout();
310     
311         WebCore::Document* doc = m_impl->frame->document();
312         
313         if (doc) {
314             wxString source = createMarkup(doc);
315             return source;
316         }
317     }
318     return wxEmptyString;
319 }
320
321 void wxWebView::SetPageSource(const wxString& source, const wxString& baseUrl)
322 {
323     if (m_impl->frame && m_impl->frame->loader()) {
324         WebCore::FrameLoader* loader = m_impl->frame->loader();
325         loader->begin(WebCore::KURL(static_cast<const char*>(baseUrl.mb_str(wxConvUTF8))));
326         loader->write(source);
327         loader->end();
328     }
329 }
330
331 wxString wxWebView::GetInnerText()
332 {
333     if (m_impl->frame->view() && m_impl->frame->view()->layoutPending())
334         m_impl->frame->view()->layout();
335         
336     WebCore::Element *documentElement = m_impl->frame->document()->documentElement();
337     return documentElement->innerText();
338 }
339
340 wxString wxWebView::GetAsMarkup()
341 {
342     if (!m_impl->frame || !m_impl->frame->document())
343         return wxEmptyString;
344
345     return createMarkup(m_impl->frame->document());
346 }
347
348 wxString wxWebView::GetExternalRepresentation()
349 {
350     if (m_impl->frame->view() && m_impl->frame->view()->layoutPending())
351         m_impl->frame->view()->layout();
352
353     return externalRepresentation(m_impl->frame->contentRenderer());
354 }
355
356 wxString wxWebView::RunScript(const wxString& javascript)
357 {
358     wxString returnValue = wxEmptyString;
359     if (m_impl->frame) {
360         KJS::JSValue* result = m_impl->frame->loader()->executeScript(javascript, true);
361         if (result)
362             returnValue = wxString(result->toString(m_impl->frame->script()->globalObject()->globalExec()).UTF8String().c_str(), wxConvUTF8);        
363     }
364     return returnValue;
365 }
366
367 void wxWebView::LoadURL(const wxString& url)
368 {
369     if (m_impl->frame && m_impl->frame->loader()) {
370         WebCore::KURL kurl = WebCore::KURL(static_cast<const char*>(url.mb_str(wxConvUTF8)));
371         // NB: This is an ugly fix, but CURL won't load sub-resources if the
372         // protocol is omitted; sadly, it will not emit an error, either, so
373         // there's no way for us to catch this problem the correct way yet.
374         if (kurl.protocol().isEmpty()) {
375             // is it a file on disk?
376             if (wxFileExists(url)) {
377                 kurl.setProtocol("file");
378                 kurl.setPath("//" + kurl.path());
379             }
380             else {
381                 kurl.setProtocol("http");
382                 kurl.setPath("//" + kurl.path());
383             }
384         }
385         m_impl->frame->loader()->load(kurl);
386     }
387 }
388
389 bool wxWebView::GoBack()
390 {
391     if (m_impl->frame && m_impl->frame->page())
392         return m_impl->frame->page()->goBack();
393
394     return false;
395 }
396
397 bool wxWebView::GoForward()
398 {
399     if (m_impl->frame && m_impl->frame->page())
400         return m_impl->frame->page()->goForward();
401
402     return false;
403 }
404
405 bool wxWebView::CanGoBack()
406 {
407     if (m_impl->frame && m_impl->frame->page() && m_impl->frame->page()->backForwardList())
408         return m_impl->frame->page()->backForwardList()->backItem() != NULL;
409
410     return false;
411 }
412
413 bool wxWebView::CanGoForward()
414 {
415     if (m_impl->frame && m_impl->frame->page() && m_impl->frame->page()->backForwardList())
416         return m_impl->frame->page()->backForwardList()->forwardItem() != NULL;
417
418     return false;
419 }
420
421 bool wxWebView::CanIncreaseTextSize() const
422 {
423     if (m_impl->frame) {
424         if (m_textMagnifier*TextSizeMultiplierRatio <= MaximumTextSizeMultiplier)
425             return true;
426     }
427     return false;
428 }
429
430 void wxWebView::IncreaseTextSize()
431 {
432     if (CanIncreaseTextSize()) {
433         m_textMagnifier = m_textMagnifier*TextSizeMultiplierRatio;
434         m_impl->frame->setZoomFactor(m_textMagnifier, true);
435     }
436 }
437
438 bool wxWebView::CanDecreaseTextSize() const
439 {
440     if (m_impl->frame) {
441         if (m_textMagnifier/TextSizeMultiplierRatio >= MinimumTextSizeMultiplier)
442             return true;
443     }
444     return false;
445 }
446
447 void wxWebView::DecreaseTextSize()
448 {        
449     if (CanDecreaseTextSize()) {
450         m_textMagnifier = m_textMagnifier/TextSizeMultiplierRatio;
451         m_impl->frame->setZoomFactor(m_textMagnifier, true);
452     }
453 }
454
455 void wxWebView::MakeEditable(bool enable)
456 {
457     m_isEditable = enable;
458 }
459
460
461 /* 
462  * Event forwarding functions to send events down to WebCore.
463  */
464
465 void wxWebView::OnPaint(wxPaintEvent& event)
466 {
467     if (m_beingDestroyed || !m_impl->frame->view() || !m_impl->frame)
468         return;
469     
470     wxAutoBufferedPaintDC dc(this);
471
472     if (IsShown() && m_impl->frame && m_impl->frame->document()) {
473 #if USE(WXGC)
474         wxGCDC gcdc(dc);
475 #endif
476
477         if (dc.IsOk()) {
478             wxRect paintRect = GetUpdateRegion().GetBox();
479
480             WebCore::IntSize offset = m_impl->frame->view()->scrollOffset();
481 #if USE(WXGC)
482             gcdc.SetDeviceOrigin(-offset.width(), -offset.height());
483 #endif
484             dc.SetDeviceOrigin(-offset.width(), -offset.height());
485             paintRect.Offset(offset.width(), offset.height());
486
487 #if USE(WXGC)
488             WebCore::GraphicsContext* gc = new WebCore::GraphicsContext(&gcdc);
489 #else
490             WebCore::GraphicsContext* gc = new WebCore::GraphicsContext((wxWindowDC*)&dc);
491 #endif
492             if (gc && m_impl->frame->contentRenderer()) {
493                 if (m_impl->frame->view()->needsLayout())
494                     m_impl->frame->view()->layout();
495
496                 m_impl->frame->paint(gc, paintRect);
497             }
498         }
499     }
500 }
501
502 void wxWebView::OnSize(wxSizeEvent& event)
503
504     if (m_isInitialized && m_impl->frame && m_impl->frame->view()) {
505         m_impl->frame->sendResizeEvent();
506         m_impl->frame->view()->layout();
507     }
508     
509     event.Skip();
510
511 }
512
513 void wxWebView::OnMouseEvents(wxMouseEvent& event)
514 {
515     event.Skip();
516     
517     if (!m_impl->frame  && m_impl->frame->view())
518         return; 
519         
520     wxPoint globalPoint = ClientToScreen(event.GetPosition());
521
522     wxEventType type = event.GetEventType();
523     
524     if (type == wxEVT_MOUSEWHEEL) {
525         WebCore::PlatformWheelEvent wkEvent(event, globalPoint);
526         m_impl->frame->eventHandler()->handleWheelEvent(wkEvent);
527         return;
528     }
529     
530     WebCore::PlatformMouseEvent wkEvent(event, globalPoint);
531
532     if (type == wxEVT_LEFT_DOWN || type == wxEVT_MIDDLE_DOWN || type == wxEVT_RIGHT_DOWN)
533         m_impl->frame->eventHandler()->handleMousePressEvent(wkEvent);
534     
535     else if (type == wxEVT_LEFT_UP || type == wxEVT_MIDDLE_UP || type == wxEVT_RIGHT_UP || 
536                 type == wxEVT_LEFT_DCLICK || type == wxEVT_MIDDLE_DCLICK || type == wxEVT_RIGHT_DCLICK)
537         m_impl->frame->eventHandler()->handleMouseReleaseEvent(wkEvent);
538
539     else if (type == wxEVT_MOTION)
540         m_impl->frame->eventHandler()->mouseMoved(wkEvent);
541 }
542
543 bool wxWebView::CanCopy()
544 {
545     if (m_impl->frame && m_impl->frame->view())
546         return (m_impl->frame->editor()->canCopy() || m_impl->frame->editor()->canDHTMLCopy());
547
548     return false;
549 }
550
551 void wxWebView::Copy()
552 {
553     if (CanCopy())
554         m_impl->frame->editor()->copy();
555 }
556
557 bool wxWebView::CanCut()
558 {
559     if (m_impl->frame && m_impl->frame->view())
560         return (m_impl->frame->editor()->canCut() || m_impl->frame->editor()->canDHTMLCut());
561
562     return false;
563 }
564
565 void wxWebView::Cut()
566 {
567     if (CanCut())
568         m_impl->frame->editor()->cut();
569 }
570
571 bool wxWebView::CanPaste()
572 {
573     if (m_impl->frame && m_impl->frame->view())
574         return (m_impl->frame->editor()->canPaste() || m_impl->frame->editor()->canDHTMLPaste());
575
576     return false;
577 }
578
579 void wxWebView::Paste()
580 {
581     if (CanPaste())
582         m_impl->frame->editor()->paste();
583
584 }
585
586 void wxWebView::OnKeyEvents(wxKeyEvent& event)
587 {
588     if (m_impl->frame && m_impl->frame->view()) {
589         // WebCore doesn't handle these events itself, so we need to do
590         // it and not send the event down or else CTRL+C will erase the text
591         // and replace it with c.
592         if (event.CmdDown() && event.GetKeyCode() == static_cast<int>('C'))
593             Copy();
594         else if (event.CmdDown() && event.GetKeyCode() == static_cast<int>('X'))
595             Cut();
596         else if (event.CmdDown() && event.GetKeyCode() == static_cast<int>('V'))
597             Paste();
598         else {    
599             WebCore::PlatformKeyboardEvent wkEvent(event);
600             if (wkEvent.type() == WebCore::PlatformKeyboardEvent::Char && wkEvent.altKey())
601                 m_impl->frame->eventHandler()->handleAccessKey(wkEvent);
602             else
603                 m_impl->frame->eventHandler()->keyEvent(wkEvent);
604         }
605     }
606     
607     // make sure we get the character event.
608     if (event.GetEventType() != wxEVT_CHAR)
609         event.Skip();
610 }
611
612 void wxWebView::OnSetFocus(wxFocusEvent& event)
613 {
614     if (m_impl->frame)
615         m_impl->frame->selection()->setFocused(true);
616
617     event.Skip();
618 }
619
620 void wxWebView::OnKillFocus(wxFocusEvent& event)
621 {
622     if (m_impl->frame)
623         m_impl->frame->selection()->setFocused(false);
624
625     event.Skip();
626 }
627
628 void wxWebView::OnActivate(wxActivateEvent& event)
629 {
630     if (m_impl->page)
631         m_impl->page->focusController()->setActive(event.GetActive());
632
633     event.Skip();
634 }