fcc829a985b0d75705cb858005001907bf45daa1
[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     WebCore::Page* page = 0;
174
175     // FIXME: This cast is obviously not as safe as a dynamic
176     // cast, but this allows us to get around requiring RTTI
177     // support for the moment. This is only used for subframes
178     // in any case, which aren't currently supported.
179     wxWebView* parentWebView = static_cast<wxWebView*>(parent);
180     
181     if (data) {
182         parentFrame = data->ownerElement;
183         page = parentWebView->m_impl->frame->page();
184     }
185     else {
186         WebCore::EditorClientWx* editorClient = new WebCore::EditorClientWx();
187         page = new WebCore::Page(new WebCore::ChromeClientWx(), new WebCore::ContextMenuClientWx(), editorClient, new WebCore::DragClientWx(), new WebCore::InspectorClientWx());
188         editorClient->setPage(page);
189     }
190     
191     WebCore::FrameLoaderClientWx* loaderClient = new WebCore::FrameLoaderClientWx();
192     
193     m_impl->frame = new WebCore::Frame(page, parentFrame, loaderClient);
194     m_impl->frame->deref();
195     m_impl->frameView = new WebCore::FrameView(m_impl->frame.get());
196     m_impl->frameView->deref();
197     
198     m_impl->frame->setView(m_impl->frameView.get());
199     m_impl->frame->init();
200     
201     m_impl->frameView->setNativeWindow(this);
202     loaderClient->setFrame(m_impl->frame.get());
203         
204     // Default settings - we should have wxWebViewSettings class for this
205     // eventually
206     WebCore::Settings* settings = page->settings();
207     settings->setLoadsImagesAutomatically(true);
208     settings->setDefaultFixedFontSize(13);
209     settings->setDefaultFontSize(16);
210     settings->setSerifFontFamily("Times New Roman");
211     settings->setFixedFontFamily("Courier New");
212     settings->setSansSerifFontFamily("Arial");
213     settings->setStandardFontFamily("Times New Roman");
214     settings->setJavaScriptEnabled(true);
215
216     m_isInitialized = true;
217 }
218
219 wxWebView::~wxWebView()
220 {
221     m_beingDestroyed = true;
222     
223     m_impl->frame->loader()->detachFromParent();
224     
225     // This test determines whether or not the frame is a subframe
226     // or the main (top level) frame. If it's the main frame, then
227     // delete its page to keep leaks from occurring
228     if (!m_impl->frame->ownerElement()) {
229         delete m_impl->frame->page();
230     }
231     m_impl->frameView = 0;
232     m_impl->frame = 0;
233
234     delete m_impl;
235 }
236
237 void wxWebView::Stop()
238 {
239     if (m_impl->frame && m_impl->frame->loader())
240         m_impl->frame->loader()->stop();
241 }
242
243 void wxWebView::Reload()
244 {
245     if (m_impl->frame && m_impl->frame->loader())
246         m_impl->frame->loader()->reload();
247 }
248
249 wxString wxWebView::GetPageSource()
250 {
251     if (m_impl->frame) {
252         WebCore::Document* doc = m_impl->frame->document();
253         
254         if (doc) {
255             wxString source = doc->toString();
256             return source;
257         }
258     }
259     return wxEmptyString;
260 }
261
262 void wxWebView::SetPageSource(const wxString& source, const wxString& baseUrl)
263 {
264     if (m_impl->frame && m_impl->frame->loader()) {
265         WebCore::FrameLoader* loader = m_impl->frame->loader();
266         loader->begin(WebCore::KURL(static_cast<const char*>(baseUrl.mb_str(wxConvUTF8))));
267         loader->write(source);
268         loader->end();
269     }
270 }
271
272 wxString wxWebView::RunScript(const wxString& javascript)
273 {
274     wxString returnValue = wxEmptyString;
275     if (m_impl->frame) {
276         KJS::JSValue* result = m_impl->frame->loader()->executeScript(javascript, true);
277         if (result)
278             returnValue = wxString(result->toString(m_impl->frame->scriptProxy()->interpreter()->globalExec()).UTF8String().c_str(), wxConvUTF8);        
279     }
280     return returnValue;
281 }
282
283 void wxWebView::LoadURL(wxString url)
284 {
285     if (m_impl->frame && m_impl->frame->loader()) {
286         WebCore::KURL kurl = WebCore::KURL(static_cast<const char*>(url.mb_str(wxConvUTF8)));
287         // NB: This is an ugly fix, but CURL won't load sub-resources if the
288         // protocol is omitted; sadly, it will not emit an error, either, so
289         // there's no way for us to catch this problem the correct way yet.
290         if (kurl.protocol().isEmpty()) {
291             // is it a file on disk?
292             if (wxFileExists(url)) {
293                 kurl.setProtocol("file");
294                 kurl.setPath("//" + kurl.path());
295             }
296             else {
297                 kurl.setProtocol("http");
298                 kurl.setPath("//" + kurl.path());
299             }
300         }
301         m_impl->frame->loader()->load(kurl);
302     }
303 }
304
305 bool wxWebView::GoBack()
306 {
307     if (m_impl->frame && m_impl->frame->page()) {
308         return m_impl->frame->page()->goBack();
309     }
310 }
311
312 bool wxWebView::GoForward()
313 {
314     if (m_impl->frame && m_impl->frame->page())
315         return m_impl->frame->page()->goForward();
316 }
317
318 bool wxWebView::CanIncreaseTextSize() const
319 {
320     if (m_impl->frame) {
321         if (m_textMagnifier*TextSizeMultiplierRatio <= MaximumTextSizeMultiplier)
322             return true;
323     }
324     return false;
325 }
326
327 void wxWebView::IncreaseTextSize()
328 {
329     if (CanIncreaseTextSize()) {
330         m_textMagnifier = m_textMagnifier*TextSizeMultiplierRatio;
331         m_impl->frame->setZoomFactor((int)rint(m_textMagnifier*100));
332     }
333 }
334
335 bool wxWebView::CanDecreaseTextSize() const
336 {
337     if (m_impl->frame) {
338         if (m_textMagnifier/TextSizeMultiplierRatio >= MinimumTextSizeMultiplier)
339             return true;
340     }
341     return false;
342 }
343
344 void wxWebView::DecreaseTextSize()
345 {        
346     if (CanDecreaseTextSize()) {
347         m_textMagnifier = m_textMagnifier/TextSizeMultiplierRatio;
348         m_impl->frame->setZoomFactor( (int)rint(m_textMagnifier*100));
349     }
350 }
351
352 void wxWebView::MakeEditable(bool enable)
353 {
354     m_isEditable = enable;
355 }
356
357
358 /* 
359  * Event forwarding functions to send events down to WebCore.
360  */
361
362 void wxWebView::OnPaint(wxPaintEvent& event)
363 {
364     if (m_beingDestroyed || !m_impl->frameView || !m_impl->frame)
365         return;
366     
367     wxAutoBufferedPaintDC dc(this);
368     DoPrepareDC(dc);
369
370     if (IsShown() && m_impl->frame && m_impl->frame->document()) {
371 #if USE(WXGC)
372         wxGCDC gcdc(dc);
373 #endif
374
375         if (dc.IsOk()) {
376             wxRect paintRect = GetUpdateRegion().GetBox();        
377             int x = 0;
378             int y = 0;
379             GetViewStart(&x, &y);
380             int unitX = 1;
381             int unitY = 1;
382             GetScrollPixelsPerUnit(&unitX, &unitY);
383             paintRect.Offset(x * unitX, y * unitY);
384
385 #if USE(WXGC)
386             WebCore::GraphicsContext* gc = new WebCore::GraphicsContext(&gcdc);
387 #else
388             WebCore::GraphicsContext* gc = new WebCore::GraphicsContext((wxWindowDC*)&dc);
389 #endif
390             if (gc && m_impl->frame->renderer()) {
391                 // FIXME: Replace this with layoutIfNeededRecursive
392                 if (m_impl->frameView->needsLayout())
393                     m_impl->frameView->layout();
394
395                 m_impl->frame->paint(gc, paintRect);
396             }
397         }
398     }
399 }
400
401 void wxWebView::OnSize(wxSizeEvent& event)
402
403     // NOTE: this call can be expensive on heavy pages, particularly on Mac,
404     // so we probably should set a timer not put x ms between layouts.
405     
406     if (m_isInitialized && m_impl->frame && m_impl->frameView) {
407         m_impl->frameView->layout();
408     }
409     
410     event.Skip();
411
412 }
413
414 void wxWebView::OnMouseEvents(wxMouseEvent& event)
415 {
416     event.Skip();
417     
418     if (!m_impl->frame  && m_impl->frameView)
419         return; 
420         
421     wxPoint globalPoint = ClientToScreen(event.GetPosition());
422
423     wxEventType type = event.GetEventType();
424     
425     if (type == wxEVT_MOUSEWHEEL) {
426         WebCore::PlatformWheelEvent wkEvent(event, globalPoint);
427         m_impl->frame->eventHandler()->handleWheelEvent(wkEvent);
428         return;
429     }
430     
431     WebCore::PlatformMouseEvent wkEvent(event, globalPoint);
432
433     if (type == wxEVT_LEFT_DOWN || type == wxEVT_MIDDLE_DOWN || type == wxEVT_RIGHT_DOWN)
434         m_impl->frame->eventHandler()->handleMousePressEvent(wkEvent);
435     
436     else if (type == wxEVT_LEFT_UP || type == wxEVT_MIDDLE_UP || type == wxEVT_RIGHT_UP || 
437                 type == wxEVT_LEFT_DCLICK || type == wxEVT_MIDDLE_DCLICK || type == wxEVT_RIGHT_DCLICK)
438         m_impl->frame->eventHandler()->handleMouseReleaseEvent(wkEvent);
439
440     else if (type == wxEVT_MOTION)
441         m_impl->frame->eventHandler()->handleMouseMoveEvent(wkEvent);
442 }
443
444 bool wxWebView::CanCopy()
445 {
446     if (m_impl->frame && m_impl->frameView) {
447         return (m_impl->frame->editor()->canCopy() || m_impl->frame->editor()->canDHTMLCopy());
448     }
449     return false;
450 }
451
452 void wxWebView::Copy()
453 {
454     if (CanCopy()) {
455         m_impl->frame->editor()->copy();
456     }
457 }
458
459 bool wxWebView::CanCut()
460 {
461     if (m_impl->frame && m_impl->frameView) {
462         return (m_impl->frame->editor()->canCut() || m_impl->frame->editor()->canDHTMLCut());
463     }
464     return false;
465 }
466
467 void wxWebView::Cut()
468 {
469     if (CanCut()) {
470         m_impl->frame->editor()->cut();
471     }
472 }
473
474 bool wxWebView::CanPaste()
475 {
476     if (m_impl->frame && m_impl->frameView) {
477         return (m_impl->frame->editor()->canPaste() || m_impl->frame->editor()->canDHTMLPaste());
478     }
479     return false;
480 }
481
482 void wxWebView::Paste()
483 {
484     if (CanPaste()) {
485         m_impl->frame->editor()->paste();
486     }
487 }
488
489 void wxWebView::OnKeyEvents(wxKeyEvent& event)
490 {
491     if (m_impl->frame && m_impl->frameView) {
492         // WebCore doesn't handle these events itself, so we need to do
493         // it and not send the event down or else CTRL+C will erase the text
494         // and replace it with c.
495         if (event.CmdDown() && event.GetKeyCode() == static_cast<int>('C')) {
496             Copy();
497         }
498         else if (event.CmdDown() && event.GetKeyCode() == static_cast<int>('X')) {
499             Cut();
500         }
501         else if (event.CmdDown() && event.GetKeyCode() == static_cast<int>('V')) {
502             Paste();
503         }
504         else {   
505             WebCore::PlatformKeyboardEvent wkEvent(event);
506             m_impl->frame->eventHandler()->keyEvent(wkEvent);
507         }
508     }
509     
510     // make sure we get the character event.
511     if (event.GetEventType() != wxEVT_CHAR)
512         event.Skip();
513 }
514
515 void wxWebView::OnSetFocus(wxFocusEvent& event)
516 {
517     if (m_impl->frame) {
518         m_impl->frame->setWindowHasFocus(true);
519     }
520     event.Skip();
521 }
522
523 void wxWebView::OnKillFocus(wxFocusEvent& event)
524 {
525     if (m_impl->frame) {
526         m_impl->frame->setWindowHasFocus(false);
527     }
528     event.Skip();
529 }
530
531 void wxWebView::OnActivate(wxActivateEvent& event)
532 {
533     if (m_impl->frame) {
534         m_impl->frame->setIsActive(event.GetActive());
535     }
536     event.Skip();
537 }