wxWebKit API changes in preparation for DRT implementation.
[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 "DeprecatedString.h"
28 #include "Document.h"
29 #include "Element.h"
30 #include "Editor.h"
31 #include "EventHandler.h"
32 #include "Frame.h"
33 #include "FrameLoader.h"
34 #include "FrameView.h"
35 #include "GraphicsContext.h"
36 #include "HTMLFrameOwnerElement.h"
37 #include "Logging.h"
38 #include "markup.h"
39 #include "Page.h"
40 #include "PlatformKeyboardEvent.h"
41 #include "PlatformMouseEvent.h"
42 #include "PlatformString.h"
43 #include "PlatformWheelEvent.h"
44 #include "RenderObject.h"
45 #include "RenderTreeAsText.h"
46 #include "Settings.h"
47
48 #include "ChromeClientWx.h"
49 #include "ContextMenuClientWx.h"
50 #include "DragClientWx.h"
51 #include "EditorClientWx.h"
52 #include "FrameLoaderClientWx.h"
53 #include "InspectorClientWx.h"
54
55 #include "kjs_proxy.h"
56 #include "kjs_binding.h"
57 #include <kjs/value.h>
58 #include <kjs/ustring.h>
59
60 #include "wx/wxprec.h"
61 #ifndef WX_PRECOMP
62     #include "wx/wx.h"
63 #endif
64
65 #include "WebView.h"
66 #include "WebViewPrivate.h"
67
68 #include <wx/defs.h>
69 #include <wx/dcbuffer.h>
70
71 // Match Safari's min/max zoom sizes by default
72 #define MinimumTextSizeMultiplier       0.5f
73 #define MaximumTextSizeMultiplier       3.0f
74 #define TextSizeMultiplierRatio         1.2f
75
76
77 #if defined(_MSC_VER)
78 int rint(double val)
79 {
80     return (int)(val < 0 ? val - 0.5 : val + 0.5);
81 }
82 #endif
83
84 // ----------------------------------------------------------------------------
85 // wxWebView Events
86 // ----------------------------------------------------------------------------
87
88 IMPLEMENT_DYNAMIC_CLASS(wxWebViewLoadEvent, wxCommandEvent)
89
90 DEFINE_EVENT_TYPE(wxEVT_WEBVIEW_LOAD)
91
92 wxWebViewLoadEvent::wxWebViewLoadEvent(wxWindow* win)
93 {
94     SetEventType( wxEVT_WEBVIEW_LOAD);
95     SetEventObject( win );
96     SetId(win->GetId());
97 }
98
99 IMPLEMENT_DYNAMIC_CLASS(wxWebViewBeforeLoadEvent, wxCommandEvent)
100
101 DEFINE_EVENT_TYPE(wxEVT_WEBVIEW_BEFORE_LOAD)
102
103 wxWebViewBeforeLoadEvent::wxWebViewBeforeLoadEvent(wxWindow* win)
104 {
105     m_cancelled = false;
106     SetEventType(wxEVT_WEBVIEW_BEFORE_LOAD);
107     SetEventObject(win);
108     SetId(win->GetId());
109 }
110
111 IMPLEMENT_DYNAMIC_CLASS(wxWebViewNewWindowEvent, wxCommandEvent)
112
113 DEFINE_EVENT_TYPE(wxEVT_WEBVIEW_NEW_WINDOW)
114
115 wxWebViewNewWindowEvent::wxWebViewNewWindowEvent(wxWindow* win)
116 {
117     SetEventType(wxEVT_WEBVIEW_NEW_WINDOW);
118     SetEventObject(win);
119     SetId(win->GetId());
120 }
121
122 IMPLEMENT_DYNAMIC_CLASS(wxWebViewRightClickEvent, wxCommandEvent)
123
124 DEFINE_EVENT_TYPE(wxEVT_WEBVIEW_RIGHT_CLICK)
125
126 wxWebViewRightClickEvent::wxWebViewRightClickEvent(wxWindow* win)
127 {
128     SetEventType(wxEVT_WEBVIEW_RIGHT_CLICK);
129     SetEventObject(win);
130     SetId(win->GetId());
131 }
132
133 IMPLEMENT_DYNAMIC_CLASS(wxWebViewConsoleMessageEvent, wxCommandEvent)
134
135 DEFINE_EVENT_TYPE(wxEVT_WEBVIEW_CONSOLE_MESSAGE)
136
137 wxWebViewConsoleMessageEvent::wxWebViewConsoleMessageEvent(wxWindow* win)
138 {
139     SetEventType(wxEVT_WEBVIEW_CONSOLE_MESSAGE);
140     SetEventObject(win);
141     SetId(win->GetId());
142 }
143
144 //---------------------------------------------------------
145 // DOM Element info data type
146 //---------------------------------------------------------
147
148 wxWebViewDOMElementInfo::wxWebViewDOMElementInfo() :
149     m_domElement(NULL),
150     m_isSelected(false),
151     m_text(wxEmptyString),
152     m_imageSrc(wxEmptyString),
153     m_link(wxEmptyString)
154 {
155 }
156
157 BEGIN_EVENT_TABLE(wxWebView, wxScrolledWindow)
158     EVT_PAINT(wxWebView::OnPaint)
159     EVT_SIZE(wxWebView::OnSize)
160     EVT_MOUSE_EVENTS(wxWebView::OnMouseEvents)
161     EVT_KEY_DOWN(wxWebView::OnKeyEvents)
162     EVT_KEY_UP(wxWebView::OnKeyEvents)
163     EVT_CHAR(wxWebView::OnKeyEvents)
164     EVT_SET_FOCUS(wxWebView::OnSetFocus)
165     EVT_KILL_FOCUS(wxWebView::OnKillFocus)
166     EVT_ACTIVATE(wxWebView::OnActivate)
167 END_EVENT_TABLE()
168
169 wxWebView::wxWebView(wxWindow* parent, int id, const wxPoint& position, 
170                     const wxSize& size, WebViewFrameData* data) :
171     m_textMagnifier(1.0),
172     m_isEditable(false),
173     m_isInitialized(false),
174     m_beingDestroyed(false),
175     m_title(wxEmptyString)
176 {
177     if (!wxScrolledWindow::Create(parent, id, position, size))
178         return;
179     
180     // this helps reduce flicker on platforms like MSW
181     SetBackgroundStyle(wxBG_STYLE_CUSTOM);
182
183     m_impl = new WebViewPrivate();
184
185     WebCore::InitializeLoggingChannelsIfNecessary();    
186     WebCore::HTMLFrameOwnerElement* parentFrame = 0;
187
188     // FIXME: This cast is obviously not as safe as a dynamic
189     // cast, but this allows us to get around requiring RTTI
190     // support for the moment. This is only used for subframes
191     // in any case, which aren't currently supported.
192     wxWebView* parentWebView = static_cast<wxWebView*>(parent);
193     
194     if (data) {
195         parentFrame = data->ownerElement;
196         m_impl->page = parentWebView->m_impl->frame->page();
197     }
198     else {
199         WebCore::EditorClientWx* editorClient = new WebCore::EditorClientWx();
200         m_impl->page = new WebCore::Page(new WebCore::ChromeClientWx(this), new WebCore::ContextMenuClientWx(), editorClient, new WebCore::DragClientWx(), new WebCore::InspectorClientWx());
201         editorClient->setPage(m_impl->page);
202     }
203     
204     WebCore::FrameLoaderClientWx* loaderClient = new WebCore::FrameLoaderClientWx();
205     
206     m_impl->frame = new WebCore::Frame(m_impl->page, parentFrame, loaderClient);
207     m_impl->frame->deref();
208     m_impl->frameView = new WebCore::FrameView(m_impl->frame.get());
209     m_impl->frameView->deref();
210     
211     m_impl->frame->setView(m_impl->frameView.get());
212     m_impl->frame->init();
213     
214     m_impl->frameView->setNativeWindow(this);
215     loaderClient->setFrame(m_impl->frame.get());
216         
217     // Default settings - we should have wxWebViewSettings class for this
218     // eventually
219     WebCore::Settings* settings = m_impl->page->settings();
220     settings->setLoadsImagesAutomatically(true);
221     settings->setDefaultFixedFontSize(13);
222     settings->setDefaultFontSize(16);
223     settings->setSerifFontFamily("Times New Roman");
224     settings->setFixedFontFamily("Courier New");
225     settings->setSansSerifFontFamily("Arial");
226     settings->setStandardFontFamily("Times New Roman");
227     settings->setJavaScriptEnabled(true);
228
229     m_isInitialized = true;
230 }
231
232 wxWebView::~wxWebView()
233 {
234     m_beingDestroyed = true;
235     
236     m_impl->frame->loader()->detachFromParent();
237     
238     delete m_impl->page;
239     m_impl->page = 0;
240     // Since frameView has the last reference to Frame, once it is
241     // destroyed the destructor for Frame will happen as well.
242     m_impl->frameView = 0;    
243 }
244
245 void wxWebView::Stop()
246 {
247     if (m_impl->frame && m_impl->frame->loader())
248         m_impl->frame->loader()->stop();
249 }
250
251 void wxWebView::Reload()
252 {
253     if (m_impl->frame && m_impl->frame->loader())
254         m_impl->frame->loader()->reload();
255 }
256
257 wxString wxWebView::GetPageSource()
258 {
259     if (m_impl->frame) {
260         if (m_impl->frameView && m_impl->frameView->layoutPending())
261             m_impl->frameView->layout();
262     
263         WebCore::Document* doc = m_impl->frame->document();
264         
265         if (doc) {
266             wxString source = doc->toString();
267             return source;
268         }
269     }
270     return wxEmptyString;
271 }
272
273 void wxWebView::SetPageSource(const wxString& source, const wxString& baseUrl)
274 {
275     if (m_impl->frame && m_impl->frame->loader()) {
276         WebCore::FrameLoader* loader = m_impl->frame->loader();
277         loader->begin(WebCore::KURL(static_cast<const char*>(baseUrl.mb_str(wxConvUTF8))));
278         loader->write(source);
279         loader->end();
280     }
281 }
282
283 wxString wxWebView::GetInnerText()
284 {
285     if (m_impl->frameView && m_impl->frameView->layoutPending())
286         m_impl->frameView->layout();
287         
288     WebCore::Element *documentElement = m_impl->frame->document()->documentElement();
289     return documentElement->innerText();
290 }
291
292 wxString wxWebView::GetAsMarkup()
293 {
294     if (!m_impl->frame || !m_impl->frame->document())
295         return wxEmptyString;
296
297     return createMarkup(m_impl->frame->document());
298 }
299
300 wxString wxWebView::GetExternalRepresentation()
301 {
302     if (m_impl->frameView && m_impl->frameView->layoutPending())
303         m_impl->frameView->layout();
304
305     return externalRepresentation(m_impl->frame->renderer());
306 }
307
308 wxString wxWebView::RunScript(const wxString& javascript)
309 {
310     wxString returnValue = wxEmptyString;
311     if (m_impl->frame) {
312         KJS::JSValue* result = m_impl->frame->loader()->executeScript(javascript, true);
313         if (result)
314             returnValue = wxString(result->toString(m_impl->frame->scriptProxy()->globalObject()->globalExec()).UTF8String().c_str(), wxConvUTF8);        
315     }
316     return returnValue;
317 }
318
319 void wxWebView::LoadURL(wxString url)
320 {
321     if (m_impl->frame && m_impl->frame->loader()) {
322         WebCore::KURL kurl = WebCore::KURL(static_cast<const char*>(url.mb_str(wxConvUTF8)));
323         // NB: This is an ugly fix, but CURL won't load sub-resources if the
324         // protocol is omitted; sadly, it will not emit an error, either, so
325         // there's no way for us to catch this problem the correct way yet.
326         if (kurl.protocol().isEmpty()) {
327             // is it a file on disk?
328             if (wxFileExists(url)) {
329                 kurl.setProtocol("file");
330                 kurl.setPath("//" + kurl.path());
331             }
332             else {
333                 kurl.setProtocol("http");
334                 kurl.setPath("//" + kurl.path());
335             }
336         }
337         m_impl->frame->loader()->load(kurl);
338     }
339 }
340
341 bool wxWebView::GoBack()
342 {
343     if (m_impl->frame && m_impl->frame->page()) {
344         return m_impl->frame->page()->goBack();
345     }
346 }
347
348 bool wxWebView::GoForward()
349 {
350     if (m_impl->frame && m_impl->frame->page())
351         return m_impl->frame->page()->goForward();
352 }
353
354 bool wxWebView::CanIncreaseTextSize() const
355 {
356     if (m_impl->frame) {
357         if (m_textMagnifier*TextSizeMultiplierRatio <= MaximumTextSizeMultiplier)
358             return true;
359     }
360     return false;
361 }
362
363 void wxWebView::IncreaseTextSize()
364 {
365     if (CanIncreaseTextSize()) {
366         m_textMagnifier = m_textMagnifier*TextSizeMultiplierRatio;
367         m_impl->frame->setZoomFactor((int)rint(m_textMagnifier*100));
368     }
369 }
370
371 bool wxWebView::CanDecreaseTextSize() const
372 {
373     if (m_impl->frame) {
374         if (m_textMagnifier/TextSizeMultiplierRatio >= MinimumTextSizeMultiplier)
375             return true;
376     }
377     return false;
378 }
379
380 void wxWebView::DecreaseTextSize()
381 {        
382     if (CanDecreaseTextSize()) {
383         m_textMagnifier = m_textMagnifier/TextSizeMultiplierRatio;
384         m_impl->frame->setZoomFactor( (int)rint(m_textMagnifier*100));
385     }
386 }
387
388 void wxWebView::MakeEditable(bool enable)
389 {
390     m_isEditable = enable;
391 }
392
393
394 /* 
395  * Event forwarding functions to send events down to WebCore.
396  */
397
398 void wxWebView::OnPaint(wxPaintEvent& event)
399 {
400     if (m_beingDestroyed || !m_impl->frameView || !m_impl->frame)
401         return;
402     
403     wxAutoBufferedPaintDC dc(this);
404
405     if (IsShown() && m_impl->frame && m_impl->frame->document()) {
406 #if USE(WXGC)
407         wxGCDC gcdc(dc);
408         DoPrepareDC(gcdc);
409 #else
410         DoPrepareDC(dc);
411 #endif
412
413         if (dc.IsOk()) {
414             wxRect paintRect = GetUpdateRegion().GetBox();        
415             int x = 0;
416             int y = 0;
417             GetViewStart(&x, &y);
418             int unitX = 1;
419             int unitY = 1;
420             GetScrollPixelsPerUnit(&unitX, &unitY);
421             paintRect.Offset(x * unitX, y * unitY);
422
423 #if USE(WXGC)
424             WebCore::GraphicsContext* gc = new WebCore::GraphicsContext(&gcdc);
425 #else
426             WebCore::GraphicsContext* gc = new WebCore::GraphicsContext((wxWindowDC*)&dc);
427 #endif
428             if (gc && m_impl->frame->renderer()) {
429                 // FIXME: Replace this with layoutIfNeededRecursive
430                 if (m_impl->frameView->needsLayout())
431                     m_impl->frameView->layout();
432
433                 m_impl->frame->paint(gc, paintRect);
434             }
435         }
436     }
437 }
438
439 void wxWebView::OnSize(wxSizeEvent& event)
440
441     // NOTE: this call can be expensive on heavy pages, particularly on Mac,
442     // so we probably should set a timer not put x ms between layouts.
443     
444     if (m_isInitialized && m_impl->frame && m_impl->frameView) {
445         m_impl->frameView->layout();
446     }
447     
448     event.Skip();
449
450 }
451
452 void wxWebView::OnMouseEvents(wxMouseEvent& event)
453 {
454     event.Skip();
455     
456     if (!m_impl->frame  && m_impl->frameView)
457         return; 
458         
459     wxPoint globalPoint = ClientToScreen(event.GetPosition());
460
461     wxEventType type = event.GetEventType();
462     
463     if (type == wxEVT_MOUSEWHEEL) {
464         WebCore::PlatformWheelEvent wkEvent(event, globalPoint);
465         m_impl->frame->eventHandler()->handleWheelEvent(wkEvent);
466         return;
467     }
468     
469     WebCore::PlatformMouseEvent wkEvent(event, globalPoint);
470
471     if (type == wxEVT_LEFT_DOWN || type == wxEVT_MIDDLE_DOWN || type == wxEVT_RIGHT_DOWN)
472         m_impl->frame->eventHandler()->handleMousePressEvent(wkEvent);
473     
474     else if (type == wxEVT_LEFT_UP || type == wxEVT_MIDDLE_UP || type == wxEVT_RIGHT_UP || 
475                 type == wxEVT_LEFT_DCLICK || type == wxEVT_MIDDLE_DCLICK || type == wxEVT_RIGHT_DCLICK)
476         m_impl->frame->eventHandler()->handleMouseReleaseEvent(wkEvent);
477
478     else if (type == wxEVT_MOTION)
479         m_impl->frame->eventHandler()->handleMouseMoveEvent(wkEvent);
480 }
481
482 bool wxWebView::CanCopy()
483 {
484     if (m_impl->frame && m_impl->frameView) {
485         return (m_impl->frame->editor()->canCopy() || m_impl->frame->editor()->canDHTMLCopy());
486     }
487     return false;
488 }
489
490 void wxWebView::Copy()
491 {
492     if (CanCopy()) {
493         m_impl->frame->editor()->copy();
494     }
495 }
496
497 bool wxWebView::CanCut()
498 {
499     if (m_impl->frame && m_impl->frameView) {
500         return (m_impl->frame->editor()->canCut() || m_impl->frame->editor()->canDHTMLCut());
501     }
502     return false;
503 }
504
505 void wxWebView::Cut()
506 {
507     if (CanCut()) {
508         m_impl->frame->editor()->cut();
509     }
510 }
511
512 bool wxWebView::CanPaste()
513 {
514     if (m_impl->frame && m_impl->frameView) {
515         return (m_impl->frame->editor()->canPaste() || m_impl->frame->editor()->canDHTMLPaste());
516     }
517     return false;
518 }
519
520 void wxWebView::Paste()
521 {
522     if (CanPaste()) {
523         m_impl->frame->editor()->paste();
524     }
525 }
526
527 void wxWebView::OnKeyEvents(wxKeyEvent& event)
528 {
529     if (m_impl->frame && m_impl->frameView) {
530         // WebCore doesn't handle these events itself, so we need to do
531         // it and not send the event down or else CTRL+C will erase the text
532         // and replace it with c.
533         if (event.CmdDown() && event.GetKeyCode() == static_cast<int>('C')) {
534             Copy();
535         }
536         else if (event.CmdDown() && event.GetKeyCode() == static_cast<int>('X')) {
537             Cut();
538         }
539         else if (event.CmdDown() && event.GetKeyCode() == static_cast<int>('V')) {
540             Paste();
541         }
542         else {   
543             WebCore::PlatformKeyboardEvent wkEvent(event);
544             if (wkEvent.type() == WebCore::PlatformKeyboardEvent::Char && wkEvent.altKey())
545                 m_impl->frame->eventHandler()->handleAccessKey(wkEvent);
546             else
547                 m_impl->frame->eventHandler()->keyEvent(wkEvent);
548         }
549     }
550     
551     // make sure we get the character event.
552     if (event.GetEventType() != wxEVT_CHAR)
553         event.Skip();
554 }
555
556 void wxWebView::OnSetFocus(wxFocusEvent& event)
557 {
558     if (m_impl->frame) {
559         m_impl->frame->setWindowHasFocus(true);
560     }
561     event.Skip();
562 }
563
564 void wxWebView::OnKillFocus(wxFocusEvent& event)
565 {
566     if (m_impl->frame) {
567         m_impl->frame->setWindowHasFocus(false);
568     }
569     event.Skip();
570 }
571
572 void wxWebView::OnActivate(wxActivateEvent& event)
573 {
574     if (m_impl->frame) {
575         m_impl->frame->setIsActive(event.GetActive());
576     }
577     event.Skip();
578 }