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