e7a11cd3db4874e35c2adc52afa44394d167e528
[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     m_impl->frameView = new WebCore::FrameView(m_impl->frame.get());
225     m_impl->frameView->deref();
226     
227     m_impl->frame->setView(m_impl->frameView.get());
228     m_impl->frame->init();
229     
230     m_impl->frameView->setNativeWindow(this);
231     loaderClient->setFrame(m_impl->frame.get());
232         
233     // Default settings - we should have wxWebViewSettings class for this
234     // eventually
235     WebCore::Settings* settings = m_impl->page->settings();
236     settings->setLoadsImagesAutomatically(true);
237     settings->setDefaultFixedFontSize(13);
238     settings->setDefaultFontSize(16);
239     settings->setSerifFontFamily("Times New Roman");
240     settings->setFixedFontFamily("Courier New");
241     settings->setSansSerifFontFamily("Arial");
242     settings->setStandardFontFamily("Times New Roman");
243     settings->setJavaScriptEnabled(true);
244
245     m_isInitialized = true;
246 }
247
248 wxWebView::~wxWebView()
249 {
250     m_beingDestroyed = true;
251     
252     m_impl->frame->loader()->detachFromParent();
253     
254     delete m_impl->page;
255     m_impl->page = 0;
256     // Since frameView has the last reference to Frame, once it is
257     // destroyed the destructor for Frame will happen as well.
258     m_impl->frameView = 0;    
259 }
260
261 void wxWebView::Stop()
262 {
263     if (m_impl->frame && m_impl->frame->loader())
264         m_impl->frame->loader()->stop();
265 }
266
267 void wxWebView::Reload()
268 {
269     if (m_impl->frame && m_impl->frame->loader())
270         m_impl->frame->loader()->reload();
271 }
272
273 wxString wxWebView::GetPageSource()
274 {
275     if (m_impl->frame) {
276         if (m_impl->frameView && m_impl->frameView->layoutPending())
277             m_impl->frameView->layout();
278     
279         WebCore::Document* doc = m_impl->frame->document();
280         
281         if (doc) {
282             wxString source = doc->toString();
283             return source;
284         }
285     }
286     return wxEmptyString;
287 }
288
289 void wxWebView::SetPageSource(const wxString& source, const wxString& baseUrl)
290 {
291     if (m_impl->frame && m_impl->frame->loader()) {
292         WebCore::FrameLoader* loader = m_impl->frame->loader();
293         loader->begin(WebCore::KURL(static_cast<const char*>(baseUrl.mb_str(wxConvUTF8))));
294         loader->write(source);
295         loader->end();
296     }
297 }
298
299 wxString wxWebView::GetInnerText()
300 {
301     if (m_impl->frameView && m_impl->frameView->layoutPending())
302         m_impl->frameView->layout();
303         
304     WebCore::Element *documentElement = m_impl->frame->document()->documentElement();
305     return documentElement->innerText();
306 }
307
308 wxString wxWebView::GetAsMarkup()
309 {
310     if (!m_impl->frame || !m_impl->frame->document())
311         return wxEmptyString;
312
313     return createMarkup(m_impl->frame->document());
314 }
315
316 wxString wxWebView::GetExternalRepresentation()
317 {
318     if (m_impl->frameView && m_impl->frameView->layoutPending())
319         m_impl->frameView->layout();
320
321     return externalRepresentation(m_impl->frame->contentRenderer());
322 }
323
324 wxString wxWebView::RunScript(const wxString& javascript)
325 {
326     wxString returnValue = wxEmptyString;
327     if (m_impl->frame) {
328         KJS::JSValue* result = m_impl->frame->loader()->executeScript(javascript, true);
329         if (result)
330             returnValue = wxString(result->toString(m_impl->frame->scriptProxy()->globalObject()->globalExec()).UTF8String().c_str(), wxConvUTF8);        
331     }
332     return returnValue;
333 }
334
335 void wxWebView::LoadURL(wxString url)
336 {
337     if (m_impl->frame && m_impl->frame->loader()) {
338         WebCore::KURL kurl = WebCore::KURL(static_cast<const char*>(url.mb_str(wxConvUTF8)));
339         // NB: This is an ugly fix, but CURL won't load sub-resources if the
340         // protocol is omitted; sadly, it will not emit an error, either, so
341         // there's no way for us to catch this problem the correct way yet.
342         if (kurl.protocol().isEmpty()) {
343             // is it a file on disk?
344             if (wxFileExists(url)) {
345                 kurl.setProtocol("file");
346                 kurl.setPath("//" + kurl.path());
347             }
348             else {
349                 kurl.setProtocol("http");
350                 kurl.setPath("//" + kurl.path());
351             }
352         }
353         m_impl->frame->loader()->load(kurl);
354     }
355 }
356
357 bool wxWebView::GoBack()
358 {
359     if (m_impl->frame && m_impl->frame->page())
360         return m_impl->frame->page()->goBack();
361
362     return false;
363 }
364
365 bool wxWebView::GoForward()
366 {
367     if (m_impl->frame && m_impl->frame->page())
368         return m_impl->frame->page()->goForward();
369
370     return false;
371 }
372
373 bool wxWebView::CanGoBack()
374 {
375     if (m_impl->frame && m_impl->frame->page() && m_impl->frame->page()->backForwardList())
376         return m_impl->frame->page()->backForwardList()->backItem() != NULL;
377
378     return false;
379 }
380
381 bool wxWebView::CanGoForward()
382 {
383     if (m_impl->frame && m_impl->frame->page() && m_impl->frame->page()->backForwardList())
384         return m_impl->frame->page()->backForwardList()->forwardItem() != NULL;
385
386     return false;
387 }
388
389 bool wxWebView::CanIncreaseTextSize() const
390 {
391     if (m_impl->frame) {
392         if (m_textMagnifier*TextSizeMultiplierRatio <= MaximumTextSizeMultiplier)
393             return true;
394     }
395     return false;
396 }
397
398 void wxWebView::IncreaseTextSize()
399 {
400     if (CanIncreaseTextSize()) {
401         m_textMagnifier = m_textMagnifier*TextSizeMultiplierRatio;
402         m_impl->frame->setZoomFactor(m_textMagnifier, true);
403     }
404 }
405
406 bool wxWebView::CanDecreaseTextSize() const
407 {
408     if (m_impl->frame) {
409         if (m_textMagnifier/TextSizeMultiplierRatio >= MinimumTextSizeMultiplier)
410             return true;
411     }
412     return false;
413 }
414
415 void wxWebView::DecreaseTextSize()
416 {        
417     if (CanDecreaseTextSize()) {
418         m_textMagnifier = m_textMagnifier/TextSizeMultiplierRatio;
419         m_impl->frame->setZoomFactor(m_textMagnifier, true);
420     }
421 }
422
423 void wxWebView::MakeEditable(bool enable)
424 {
425     m_isEditable = enable;
426 }
427
428
429 /* 
430  * Event forwarding functions to send events down to WebCore.
431  */
432
433 void wxWebView::OnPaint(wxPaintEvent& event)
434 {
435     if (m_beingDestroyed || !m_impl->frameView || !m_impl->frame)
436         return;
437     
438     wxAutoBufferedPaintDC dc(this);
439
440     if (IsShown() && m_impl->frame && m_impl->frame->document()) {
441 #if USE(WXGC)
442         wxGCDC gcdc(dc);
443 #endif
444
445         if (dc.IsOk()) {
446             wxRect paintRect = GetUpdateRegion().GetBox();
447
448             WebCore::IntSize offset = m_impl->frameView->scrollOffset();
449 #if USE(WXGC)
450             gcdc.SetDeviceOrigin(-offset.width(), -offset.height());
451 #endif
452             dc.SetDeviceOrigin(-offset.width(), -offset.height());
453             paintRect.Offset(offset.width(), offset.height());
454
455 #if USE(WXGC)
456             WebCore::GraphicsContext* gc = new WebCore::GraphicsContext(&gcdc);
457 #else
458             WebCore::GraphicsContext* gc = new WebCore::GraphicsContext((wxWindowDC*)&dc);
459 #endif
460             if (gc && m_impl->frame->contentRenderer()) {
461                 if (m_impl->frameView->needsLayout())
462                     m_impl->frameView->layout();
463
464                 m_impl->frame->paint(gc, paintRect);
465             }
466         }
467     }
468 }
469
470 void wxWebView::OnSize(wxSizeEvent& event)
471
472     if (m_isInitialized && m_impl->frame && m_impl->frameView) {
473         m_impl->frame->sendResizeEvent();
474         m_impl->frameView->layout();
475     }
476     
477     event.Skip();
478
479 }
480
481 void wxWebView::OnMouseEvents(wxMouseEvent& event)
482 {
483     event.Skip();
484     
485     if (!m_impl->frame  && m_impl->frameView)
486         return; 
487         
488     wxPoint globalPoint = ClientToScreen(event.GetPosition());
489
490     wxEventType type = event.GetEventType();
491     
492     if (type == wxEVT_MOUSEWHEEL) {
493         WebCore::PlatformWheelEvent wkEvent(event, globalPoint);
494         m_impl->frame->eventHandler()->handleWheelEvent(wkEvent);
495         return;
496     }
497     
498     WebCore::PlatformMouseEvent wkEvent(event, globalPoint);
499
500     if (type == wxEVT_LEFT_DOWN || type == wxEVT_MIDDLE_DOWN || type == wxEVT_RIGHT_DOWN)
501         m_impl->frame->eventHandler()->handleMousePressEvent(wkEvent);
502     
503     else if (type == wxEVT_LEFT_UP || type == wxEVT_MIDDLE_UP || type == wxEVT_RIGHT_UP || 
504                 type == wxEVT_LEFT_DCLICK || type == wxEVT_MIDDLE_DCLICK || type == wxEVT_RIGHT_DCLICK)
505         m_impl->frame->eventHandler()->handleMouseReleaseEvent(wkEvent);
506
507     else if (type == wxEVT_MOTION)
508         m_impl->frame->eventHandler()->handleMouseMoveEvent(wkEvent);
509 }
510
511 bool wxWebView::CanCopy()
512 {
513     if (m_impl->frame && m_impl->frameView)
514         return (m_impl->frame->editor()->canCopy() || m_impl->frame->editor()->canDHTMLCopy());
515
516     return false;
517 }
518
519 void wxWebView::Copy()
520 {
521     if (CanCopy())
522         m_impl->frame->editor()->copy();
523 }
524
525 bool wxWebView::CanCut()
526 {
527     if (m_impl->frame && m_impl->frameView)
528         return (m_impl->frame->editor()->canCut() || m_impl->frame->editor()->canDHTMLCut());
529
530     return false;
531 }
532
533 void wxWebView::Cut()
534 {
535     if (CanCut())
536         m_impl->frame->editor()->cut();
537 }
538
539 bool wxWebView::CanPaste()
540 {
541     if (m_impl->frame && m_impl->frameView)
542         return (m_impl->frame->editor()->canPaste() || m_impl->frame->editor()->canDHTMLPaste());
543
544     return false;
545 }
546
547 void wxWebView::Paste()
548 {
549     if (CanPaste())
550         m_impl->frame->editor()->paste();
551
552 }
553
554 void wxWebView::OnKeyEvents(wxKeyEvent& event)
555 {
556     if (m_impl->frame && m_impl->frameView) {
557         // WebCore doesn't handle these events itself, so we need to do
558         // it and not send the event down or else CTRL+C will erase the text
559         // and replace it with c.
560         if (event.CmdDown() && event.GetKeyCode() == static_cast<int>('C'))
561             Copy();
562         else if (event.CmdDown() && event.GetKeyCode() == static_cast<int>('X'))
563             Cut();
564         else if (event.CmdDown() && event.GetKeyCode() == static_cast<int>('V'))
565             Paste();
566         else {    
567             WebCore::PlatformKeyboardEvent wkEvent(event);
568             if (wkEvent.type() == WebCore::PlatformKeyboardEvent::Char && wkEvent.altKey())
569                 m_impl->frame->eventHandler()->handleAccessKey(wkEvent);
570             else
571                 m_impl->frame->eventHandler()->keyEvent(wkEvent);
572         }
573     }
574     
575     // make sure we get the character event.
576     if (event.GetEventType() != wxEVT_CHAR)
577         event.Skip();
578 }
579
580 void wxWebView::OnSetFocus(wxFocusEvent& event)
581 {
582     if (m_impl->frame)
583         m_impl->frame->selectionController()->setFocused(true);
584
585     event.Skip();
586 }
587
588 void wxWebView::OnKillFocus(wxFocusEvent& event)
589 {
590     if (m_impl->frame)
591         m_impl->frame->selectionController()->setFocused(false);
592
593     event.Skip();
594 }
595
596 void wxWebView::OnActivate(wxActivateEvent& event)
597 {
598     if (m_impl->page)
599         m_impl->page->focusController()->setActive(event.GetActive());
600
601     event.Skip();
602 }