Reviewed by Kevin Ollivier.
[WebKit-https.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 "kjs_proxy.h"
60 #include "kjs_binding.h"
61 #include <kjs/value.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 //---------------------------------------------------------
154 // DOM Element info data type
155 //---------------------------------------------------------
156
157 wxWebViewDOMElementInfo::wxWebViewDOMElementInfo() :
158     m_domElement(NULL),
159     m_isSelected(false),
160     m_text(wxEmptyString),
161     m_imageSrc(wxEmptyString),
162     m_link(wxEmptyString)
163 {
164 }
165
166 BEGIN_EVENT_TABLE(wxWebView, wxWindow)
167     EVT_PAINT(wxWebView::OnPaint)
168     EVT_SIZE(wxWebView::OnSize)
169     EVT_MOUSE_EVENTS(wxWebView::OnMouseEvents)
170     EVT_KEY_DOWN(wxWebView::OnKeyEvents)
171     EVT_KEY_UP(wxWebView::OnKeyEvents)
172     EVT_CHAR(wxWebView::OnKeyEvents)
173     EVT_SET_FOCUS(wxWebView::OnSetFocus)
174     EVT_KILL_FOCUS(wxWebView::OnKillFocus)
175     EVT_ACTIVATE(wxWebView::OnActivate)
176 END_EVENT_TABLE()
177
178 wxWebView::wxWebView(wxWindow* parent, int id, const wxPoint& position, 
179                     const wxSize& size, WebViewFrameData* data) :
180     m_textMagnifier(1.0),
181     m_isEditable(false),
182     m_isInitialized(false),
183     m_beingDestroyed(false),
184     m_title(wxEmptyString)
185 {
186     if (!wxWindow::Create(parent, id, position, size, wxBORDER_NONE | wxHSCROLL | wxVSCROLL))
187         return;
188
189 // This is necessary because we are using SharedTimerWin.cpp on Windows,
190 // due to a problem with exceptions getting eaten when using the callback
191 // approach to timers (which wx itself uses).
192 #if __WXMSW__
193     WebCore::Page::setInstanceHandle(wxGetInstance());
194 #endif
195
196     // this helps reduce flicker on platforms like MSW
197     SetBackgroundStyle(wxBG_STYLE_CUSTOM);
198
199     m_impl = new WebViewPrivate();
200
201     WebCore::InitializeLoggingChannelsIfNecessary();    
202     WebCore::HTMLFrameOwnerElement* parentFrame = 0;
203
204     // FIXME: This cast is obviously not as safe as a dynamic
205     // cast, but this allows us to get around requiring RTTI
206     // support for the moment. This is only used for subframes
207     // in any case, which aren't currently supported.
208     wxWebView* parentWebView = static_cast<wxWebView*>(parent);
209     
210     if (data) {
211         parentFrame = data->ownerElement;
212         m_impl->page = parentWebView->m_impl->frame->page();
213     }
214     else {
215         WebCore::EditorClientWx* editorClient = new WebCore::EditorClientWx();
216         m_impl->page = new WebCore::Page(new WebCore::ChromeClientWx(this), new WebCore::ContextMenuClientWx(), editorClient, new WebCore::DragClientWx(), new WebCore::InspectorClientWx());
217         editorClient->setPage(m_impl->page);
218     }
219     
220     WebCore::FrameLoaderClientWx* loaderClient = new WebCore::FrameLoaderClientWx();
221     
222     m_impl->frame = new WebCore::Frame(m_impl->page, parentFrame, loaderClient);
223     m_impl->frame->deref();
224
225     loaderClient->setFrame(m_impl->frame.get());
226     loaderClient->setWebView(this);
227     
228     m_impl->frame->init();
229         
230     // Default settings - we should have wxWebViewSettings class for this
231     // eventually
232     WebCore::Settings* settings = m_impl->page->settings();
233     settings->setLoadsImagesAutomatically(true);
234     settings->setDefaultFixedFontSize(13);
235     settings->setDefaultFontSize(16);
236     settings->setSerifFontFamily("Times New Roman");
237     settings->setFixedFontFamily("Courier New");
238     settings->setSansSerifFontFamily("Arial");
239     settings->setStandardFontFamily("Times New Roman");
240     settings->setJavaScriptEnabled(true);
241
242     m_isInitialized = true;
243 }
244
245 wxWebView::~wxWebView()
246 {
247     m_beingDestroyed = true;
248     
249     m_impl->frame->loader()->detachFromParent();
250     
251     delete m_impl->page;
252     m_impl->page = 0;   
253 }
254
255 void wxWebView::Stop()
256 {
257     if (m_impl->frame && m_impl->frame->loader())
258         m_impl->frame->loader()->stop();
259 }
260
261 void wxWebView::Reload()
262 {
263     if (m_impl->frame && m_impl->frame->loader())
264         m_impl->frame->loader()->reload();
265 }
266
267 wxString wxWebView::GetPageSource()
268 {
269     if (m_impl->frame) {
270         if (m_impl->frame->view() && m_impl->frame->view()->layoutPending())
271             m_impl->frame->view()->layout();
272     
273         WebCore::Document* doc = m_impl->frame->document();
274         
275         if (doc) {
276             wxString source = doc->toString();
277             return source;
278         }
279     }
280     return wxEmptyString;
281 }
282
283 void wxWebView::SetPageSource(const wxString& source, const wxString& baseUrl)
284 {
285     if (m_impl->frame && m_impl->frame->loader()) {
286         WebCore::FrameLoader* loader = m_impl->frame->loader();
287         loader->begin(WebCore::KURL(static_cast<const char*>(baseUrl.mb_str(wxConvUTF8))));
288         loader->write(source);
289         loader->end();
290     }
291 }
292
293 wxString wxWebView::GetInnerText()
294 {
295     if (m_impl->frame->view() && m_impl->frame->view()->layoutPending())
296         m_impl->frame->view()->layout();
297         
298     WebCore::Element *documentElement = m_impl->frame->document()->documentElement();
299     return documentElement->innerText();
300 }
301
302 wxString wxWebView::GetAsMarkup()
303 {
304     if (!m_impl->frame || !m_impl->frame->document())
305         return wxEmptyString;
306
307     return createMarkup(m_impl->frame->document());
308 }
309
310 wxString wxWebView::GetExternalRepresentation()
311 {
312     if (m_impl->frame->view() && m_impl->frame->view()->layoutPending())
313         m_impl->frame->view()->layout();
314
315     return externalRepresentation(m_impl->frame->contentRenderer());
316 }
317
318 wxString wxWebView::RunScript(const wxString& javascript)
319 {
320     wxString returnValue = wxEmptyString;
321     if (m_impl->frame) {
322         KJS::JSValue* result = m_impl->frame->loader()->executeScript(javascript, true);
323         if (result)
324             returnValue = wxString(result->toString(m_impl->frame->scriptProxy()->globalObject()->globalExec()).UTF8String().c_str(), wxConvUTF8);        
325     }
326     return returnValue;
327 }
328
329 void wxWebView::LoadURL(wxString url)
330 {
331     if (m_impl->frame && m_impl->frame->loader()) {
332         WebCore::KURL kurl = WebCore::KURL(static_cast<const char*>(url.mb_str(wxConvUTF8)));
333         // NB: This is an ugly fix, but CURL won't load sub-resources if the
334         // protocol is omitted; sadly, it will not emit an error, either, so
335         // there's no way for us to catch this problem the correct way yet.
336         if (kurl.protocol().isEmpty()) {
337             // is it a file on disk?
338             if (wxFileExists(url)) {
339                 kurl.setProtocol("file");
340                 kurl.setPath("//" + kurl.path());
341             }
342             else {
343                 kurl.setProtocol("http");
344                 kurl.setPath("//" + kurl.path());
345             }
346         }
347         m_impl->frame->loader()->load(kurl);
348     }
349 }
350
351 bool wxWebView::GoBack()
352 {
353     if (m_impl->frame && m_impl->frame->page())
354         return m_impl->frame->page()->goBack();
355
356     return false;
357 }
358
359 bool wxWebView::GoForward()
360 {
361     if (m_impl->frame && m_impl->frame->page())
362         return m_impl->frame->page()->goForward();
363
364     return false;
365 }
366
367 bool wxWebView::CanGoBack()
368 {
369     if (m_impl->frame && m_impl->frame->page() && m_impl->frame->page()->backForwardList())
370         return m_impl->frame->page()->backForwardList()->backItem() != NULL;
371
372     return false;
373 }
374
375 bool wxWebView::CanGoForward()
376 {
377     if (m_impl->frame && m_impl->frame->page() && m_impl->frame->page()->backForwardList())
378         return m_impl->frame->page()->backForwardList()->forwardItem() != NULL;
379
380     return false;
381 }
382
383 bool wxWebView::CanIncreaseTextSize() const
384 {
385     if (m_impl->frame) {
386         if (m_textMagnifier*TextSizeMultiplierRatio <= MaximumTextSizeMultiplier)
387             return true;
388     }
389     return false;
390 }
391
392 void wxWebView::IncreaseTextSize()
393 {
394     if (CanIncreaseTextSize()) {
395         m_textMagnifier = m_textMagnifier*TextSizeMultiplierRatio;
396         m_impl->frame->setZoomFactor(m_textMagnifier, true);
397     }
398 }
399
400 bool wxWebView::CanDecreaseTextSize() const
401 {
402     if (m_impl->frame) {
403         if (m_textMagnifier/TextSizeMultiplierRatio >= MinimumTextSizeMultiplier)
404             return true;
405     }
406     return false;
407 }
408
409 void wxWebView::DecreaseTextSize()
410 {        
411     if (CanDecreaseTextSize()) {
412         m_textMagnifier = m_textMagnifier/TextSizeMultiplierRatio;
413         m_impl->frame->setZoomFactor(m_textMagnifier, true);
414     }
415 }
416
417 void wxWebView::MakeEditable(bool enable)
418 {
419     m_isEditable = enable;
420 }
421
422
423 /* 
424  * Event forwarding functions to send events down to WebCore.
425  */
426
427 void wxWebView::OnPaint(wxPaintEvent& event)
428 {
429     if (m_beingDestroyed || !m_impl->frame->view() || !m_impl->frame)
430         return;
431     
432     wxAutoBufferedPaintDC dc(this);
433
434     if (IsShown() && m_impl->frame && m_impl->frame->document()) {
435 #if USE(WXGC)
436         wxGCDC gcdc(dc);
437 #endif
438
439         if (dc.IsOk()) {
440             wxRect paintRect = GetUpdateRegion().GetBox();
441
442             WebCore::IntSize offset = m_impl->frame->view()->scrollOffset();
443 #if USE(WXGC)
444             gcdc.SetDeviceOrigin(-offset.width(), -offset.height());
445 #endif
446             dc.SetDeviceOrigin(-offset.width(), -offset.height());
447             paintRect.Offset(offset.width(), offset.height());
448
449 #if USE(WXGC)
450             WebCore::GraphicsContext* gc = new WebCore::GraphicsContext(&gcdc);
451 #else
452             WebCore::GraphicsContext* gc = new WebCore::GraphicsContext((wxWindowDC*)&dc);
453 #endif
454             if (gc && m_impl->frame->contentRenderer()) {
455                 if (m_impl->frame->view()->needsLayout())
456                     m_impl->frame->view()->layout();
457
458                 m_impl->frame->paint(gc, paintRect);
459             }
460         }
461     }
462 }
463
464 void wxWebView::OnSize(wxSizeEvent& event)
465
466     if (m_isInitialized && m_impl->frame && m_impl->frame->view()) {
467         m_impl->frame->sendResizeEvent();
468         m_impl->frame->view()->layout();
469     }
470     
471     event.Skip();
472
473 }
474
475 void wxWebView::OnMouseEvents(wxMouseEvent& event)
476 {
477     event.Skip();
478     
479     if (!m_impl->frame  && m_impl->frame->view())
480         return; 
481         
482     wxPoint globalPoint = ClientToScreen(event.GetPosition());
483
484     wxEventType type = event.GetEventType();
485     
486     if (type == wxEVT_MOUSEWHEEL) {
487         WebCore::PlatformWheelEvent wkEvent(event, globalPoint);
488         m_impl->frame->eventHandler()->handleWheelEvent(wkEvent);
489         return;
490     }
491     
492     WebCore::PlatformMouseEvent wkEvent(event, globalPoint);
493
494     if (type == wxEVT_LEFT_DOWN || type == wxEVT_MIDDLE_DOWN || type == wxEVT_RIGHT_DOWN)
495         m_impl->frame->eventHandler()->handleMousePressEvent(wkEvent);
496     
497     else if (type == wxEVT_LEFT_UP || type == wxEVT_MIDDLE_UP || type == wxEVT_RIGHT_UP || 
498                 type == wxEVT_LEFT_DCLICK || type == wxEVT_MIDDLE_DCLICK || type == wxEVT_RIGHT_DCLICK)
499         m_impl->frame->eventHandler()->handleMouseReleaseEvent(wkEvent);
500
501     else if (type == wxEVT_MOTION)
502         m_impl->frame->eventHandler()->handleMouseMoveEvent(wkEvent);
503 }
504
505 bool wxWebView::CanCopy()
506 {
507     if (m_impl->frame && m_impl->frame->view())
508         return (m_impl->frame->editor()->canCopy() || m_impl->frame->editor()->canDHTMLCopy());
509
510     return false;
511 }
512
513 void wxWebView::Copy()
514 {
515     if (CanCopy())
516         m_impl->frame->editor()->copy();
517 }
518
519 bool wxWebView::CanCut()
520 {
521     if (m_impl->frame && m_impl->frame->view())
522         return (m_impl->frame->editor()->canCut() || m_impl->frame->editor()->canDHTMLCut());
523
524     return false;
525 }
526
527 void wxWebView::Cut()
528 {
529     if (CanCut())
530         m_impl->frame->editor()->cut();
531 }
532
533 bool wxWebView::CanPaste()
534 {
535     if (m_impl->frame && m_impl->frame->view())
536         return (m_impl->frame->editor()->canPaste() || m_impl->frame->editor()->canDHTMLPaste());
537
538     return false;
539 }
540
541 void wxWebView::Paste()
542 {
543     if (CanPaste())
544         m_impl->frame->editor()->paste();
545
546 }
547
548 void wxWebView::OnKeyEvents(wxKeyEvent& event)
549 {
550     if (m_impl->frame && m_impl->frame->view()) {
551         // WebCore doesn't handle these events itself, so we need to do
552         // it and not send the event down or else CTRL+C will erase the text
553         // and replace it with c.
554         if (event.CmdDown() && event.GetKeyCode() == static_cast<int>('C'))
555             Copy();
556         else if (event.CmdDown() && event.GetKeyCode() == static_cast<int>('X'))
557             Cut();
558         else if (event.CmdDown() && event.GetKeyCode() == static_cast<int>('V'))
559             Paste();
560         else {    
561             WebCore::PlatformKeyboardEvent wkEvent(event);
562             if (wkEvent.type() == WebCore::PlatformKeyboardEvent::Char && wkEvent.altKey())
563                 m_impl->frame->eventHandler()->handleAccessKey(wkEvent);
564             else
565                 m_impl->frame->eventHandler()->keyEvent(wkEvent);
566         }
567     }
568     
569     // make sure we get the character event.
570     if (event.GetEventType() != wxEVT_CHAR)
571         event.Skip();
572 }
573
574 void wxWebView::OnSetFocus(wxFocusEvent& event)
575 {
576     if (m_impl->frame)
577         m_impl->frame->selectionController()->setFocused(true);
578
579     event.Skip();
580 }
581
582 void wxWebView::OnKillFocus(wxFocusEvent& event)
583 {
584     if (m_impl->frame)
585         m_impl->frame->selectionController()->setFocused(false);
586
587     event.Skip();
588 }
589
590 void wxWebView::OnActivate(wxActivateEvent& event)
591 {
592     if (m_impl->page)
593         m_impl->page->focusController()->setActive(event.GetActive());
594
595     event.Skip();
596 }