2 * Copyright (C) 2007 Kevin Ollivier All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #include "DeprecatedString.h"
31 #include "EventHandler.h"
33 #include "FrameLoader.h"
34 #include "FrameView.h"
35 #include "GraphicsContext.h"
36 #include "HTMLFrameOwnerElement.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"
48 #include "ChromeClientWx.h"
49 #include "ContextMenuClientWx.h"
50 #include "DragClientWx.h"
51 #include "EditorClientWx.h"
52 #include "FrameLoaderClientWx.h"
53 #include "InspectorClientWx.h"
55 #include "kjs_proxy.h"
56 #include "kjs_binding.h"
57 #include <kjs/value.h>
58 #include <kjs/ustring.h>
60 #include "wx/wxprec.h"
66 #include "WebViewPrivate.h"
69 #include <wx/dcbuffer.h>
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
80 return (int)(val < 0 ? val - 0.5 : val + 0.5);
84 // ----------------------------------------------------------------------------
86 // ----------------------------------------------------------------------------
88 IMPLEMENT_DYNAMIC_CLASS(wxWebViewLoadEvent, wxCommandEvent)
90 DEFINE_EVENT_TYPE(wxEVT_WEBVIEW_LOAD)
92 wxWebViewLoadEvent::wxWebViewLoadEvent(wxWindow* win)
94 SetEventType( wxEVT_WEBVIEW_LOAD);
95 SetEventObject( win );
99 IMPLEMENT_DYNAMIC_CLASS(wxWebViewBeforeLoadEvent, wxCommandEvent)
101 DEFINE_EVENT_TYPE(wxEVT_WEBVIEW_BEFORE_LOAD)
103 wxWebViewBeforeLoadEvent::wxWebViewBeforeLoadEvent(wxWindow* win)
106 SetEventType(wxEVT_WEBVIEW_BEFORE_LOAD);
111 IMPLEMENT_DYNAMIC_CLASS(wxWebViewNewWindowEvent, wxCommandEvent)
113 DEFINE_EVENT_TYPE(wxEVT_WEBVIEW_NEW_WINDOW)
115 wxWebViewNewWindowEvent::wxWebViewNewWindowEvent(wxWindow* win)
117 SetEventType(wxEVT_WEBVIEW_NEW_WINDOW);
122 IMPLEMENT_DYNAMIC_CLASS(wxWebViewRightClickEvent, wxCommandEvent)
124 DEFINE_EVENT_TYPE(wxEVT_WEBVIEW_RIGHT_CLICK)
126 wxWebViewRightClickEvent::wxWebViewRightClickEvent(wxWindow* win)
128 SetEventType(wxEVT_WEBVIEW_RIGHT_CLICK);
133 IMPLEMENT_DYNAMIC_CLASS(wxWebViewConsoleMessageEvent, wxCommandEvent)
135 DEFINE_EVENT_TYPE(wxEVT_WEBVIEW_CONSOLE_MESSAGE)
137 wxWebViewConsoleMessageEvent::wxWebViewConsoleMessageEvent(wxWindow* win)
139 SetEventType(wxEVT_WEBVIEW_CONSOLE_MESSAGE);
144 //---------------------------------------------------------
145 // DOM Element info data type
146 //---------------------------------------------------------
148 wxWebViewDOMElementInfo::wxWebViewDOMElementInfo() :
151 m_text(wxEmptyString),
152 m_imageSrc(wxEmptyString),
153 m_link(wxEmptyString)
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)
169 wxWebView::wxWebView(wxWindow* parent, int id, const wxPoint& position,
170 const wxSize& size, WebViewFrameData* data) :
171 m_textMagnifier(1.0),
173 m_isInitialized(false),
174 m_beingDestroyed(false),
175 m_title(wxEmptyString)
177 if (!wxScrolledWindow::Create(parent, id, position, size))
180 // this helps reduce flicker on platforms like MSW
181 SetBackgroundStyle(wxBG_STYLE_CUSTOM);
183 m_impl = new WebViewPrivate();
185 WebCore::InitializeLoggingChannelsIfNecessary();
186 WebCore::HTMLFrameOwnerElement* parentFrame = 0;
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);
195 parentFrame = data->ownerElement;
196 m_impl->page = parentWebView->m_impl->frame->page();
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);
204 WebCore::FrameLoaderClientWx* loaderClient = new WebCore::FrameLoaderClientWx();
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();
211 m_impl->frame->setView(m_impl->frameView.get());
212 m_impl->frame->init();
214 m_impl->frameView->setNativeWindow(this);
215 loaderClient->setFrame(m_impl->frame.get());
217 // Default settings - we should have wxWebViewSettings class for this
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);
229 m_isInitialized = true;
232 wxWebView::~wxWebView()
234 m_beingDestroyed = true;
236 m_impl->frame->loader()->detachFromParent();
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;
245 void wxWebView::Stop()
247 if (m_impl->frame && m_impl->frame->loader())
248 m_impl->frame->loader()->stop();
251 void wxWebView::Reload()
253 if (m_impl->frame && m_impl->frame->loader())
254 m_impl->frame->loader()->reload();
257 wxString wxWebView::GetPageSource()
260 if (m_impl->frameView && m_impl->frameView->layoutPending())
261 m_impl->frameView->layout();
263 WebCore::Document* doc = m_impl->frame->document();
266 wxString source = doc->toString();
270 return wxEmptyString;
273 void wxWebView::SetPageSource(const wxString& source, const wxString& baseUrl)
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);
283 wxString wxWebView::GetInnerText()
285 if (m_impl->frameView && m_impl->frameView->layoutPending())
286 m_impl->frameView->layout();
288 WebCore::Element *documentElement = m_impl->frame->document()->documentElement();
289 return documentElement->innerText();
292 wxString wxWebView::GetAsMarkup()
294 if (!m_impl->frame || !m_impl->frame->document())
295 return wxEmptyString;
297 return createMarkup(m_impl->frame->document());
300 wxString wxWebView::GetExternalRepresentation()
302 if (m_impl->frameView && m_impl->frameView->layoutPending())
303 m_impl->frameView->layout();
305 return externalRepresentation(m_impl->frame->renderer());
308 wxString wxWebView::RunScript(const wxString& javascript)
310 wxString returnValue = wxEmptyString;
312 KJS::JSValue* result = m_impl->frame->loader()->executeScript(javascript, true);
314 returnValue = wxString(result->toString(m_impl->frame->scriptProxy()->globalObject()->globalExec()).UTF8String().c_str(), wxConvUTF8);
319 void wxWebView::LoadURL(wxString url)
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());
333 kurl.setProtocol("http");
334 kurl.setPath("//" + kurl.path());
337 m_impl->frame->loader()->load(kurl);
341 bool wxWebView::GoBack()
343 if (m_impl->frame && m_impl->frame->page()) {
344 return m_impl->frame->page()->goBack();
348 bool wxWebView::GoForward()
350 if (m_impl->frame && m_impl->frame->page())
351 return m_impl->frame->page()->goForward();
354 bool wxWebView::CanIncreaseTextSize() const
357 if (m_textMagnifier*TextSizeMultiplierRatio <= MaximumTextSizeMultiplier)
363 void wxWebView::IncreaseTextSize()
365 if (CanIncreaseTextSize()) {
366 m_textMagnifier = m_textMagnifier*TextSizeMultiplierRatio;
367 m_impl->frame->setZoomFactor((int)rint(m_textMagnifier*100));
371 bool wxWebView::CanDecreaseTextSize() const
374 if (m_textMagnifier/TextSizeMultiplierRatio >= MinimumTextSizeMultiplier)
380 void wxWebView::DecreaseTextSize()
382 if (CanDecreaseTextSize()) {
383 m_textMagnifier = m_textMagnifier/TextSizeMultiplierRatio;
384 m_impl->frame->setZoomFactor( (int)rint(m_textMagnifier*100));
388 void wxWebView::MakeEditable(bool enable)
390 m_isEditable = enable;
395 * Event forwarding functions to send events down to WebCore.
398 void wxWebView::OnPaint(wxPaintEvent& event)
400 if (m_beingDestroyed || !m_impl->frameView || !m_impl->frame)
403 wxAutoBufferedPaintDC dc(this);
405 if (IsShown() && m_impl->frame && m_impl->frame->document()) {
414 wxRect paintRect = GetUpdateRegion().GetBox();
417 GetViewStart(&x, &y);
420 GetScrollPixelsPerUnit(&unitX, &unitY);
421 paintRect.Offset(x * unitX, y * unitY);
424 WebCore::GraphicsContext* gc = new WebCore::GraphicsContext(&gcdc);
426 WebCore::GraphicsContext* gc = new WebCore::GraphicsContext((wxWindowDC*)&dc);
428 if (gc && m_impl->frame->renderer()) {
429 // FIXME: Replace this with layoutIfNeededRecursive
430 if (m_impl->frameView->needsLayout())
431 m_impl->frameView->layout();
433 m_impl->frame->paint(gc, paintRect);
439 void wxWebView::OnSize(wxSizeEvent& event)
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.
444 if (m_isInitialized && m_impl->frame && m_impl->frameView) {
445 m_impl->frameView->layout();
452 void wxWebView::OnMouseEvents(wxMouseEvent& event)
456 if (!m_impl->frame && m_impl->frameView)
459 wxPoint globalPoint = ClientToScreen(event.GetPosition());
461 wxEventType type = event.GetEventType();
463 if (type == wxEVT_MOUSEWHEEL) {
464 WebCore::PlatformWheelEvent wkEvent(event, globalPoint);
465 m_impl->frame->eventHandler()->handleWheelEvent(wkEvent);
469 WebCore::PlatformMouseEvent wkEvent(event, globalPoint);
471 if (type == wxEVT_LEFT_DOWN || type == wxEVT_MIDDLE_DOWN || type == wxEVT_RIGHT_DOWN)
472 m_impl->frame->eventHandler()->handleMousePressEvent(wkEvent);
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);
478 else if (type == wxEVT_MOTION)
479 m_impl->frame->eventHandler()->handleMouseMoveEvent(wkEvent);
482 bool wxWebView::CanCopy()
484 if (m_impl->frame && m_impl->frameView) {
485 return (m_impl->frame->editor()->canCopy() || m_impl->frame->editor()->canDHTMLCopy());
490 void wxWebView::Copy()
493 m_impl->frame->editor()->copy();
497 bool wxWebView::CanCut()
499 if (m_impl->frame && m_impl->frameView) {
500 return (m_impl->frame->editor()->canCut() || m_impl->frame->editor()->canDHTMLCut());
505 void wxWebView::Cut()
508 m_impl->frame->editor()->cut();
512 bool wxWebView::CanPaste()
514 if (m_impl->frame && m_impl->frameView) {
515 return (m_impl->frame->editor()->canPaste() || m_impl->frame->editor()->canDHTMLPaste());
520 void wxWebView::Paste()
523 m_impl->frame->editor()->paste();
527 void wxWebView::OnKeyEvents(wxKeyEvent& event)
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')) {
536 else if (event.CmdDown() && event.GetKeyCode() == static_cast<int>('X')) {
539 else if (event.CmdDown() && event.GetKeyCode() == static_cast<int>('V')) {
543 WebCore::PlatformKeyboardEvent wkEvent(event);
544 if (wkEvent.type() == WebCore::PlatformKeyboardEvent::Char && wkEvent.altKey())
545 m_impl->frame->eventHandler()->handleAccessKey(wkEvent);
547 m_impl->frame->eventHandler()->keyEvent(wkEvent);
551 // make sure we get the character event.
552 if (event.GetEventType() != wxEVT_CHAR)
556 void wxWebView::OnSetFocus(wxFocusEvent& event)
559 m_impl->frame->setWindowHasFocus(true);
564 void wxWebView::OnKillFocus(wxFocusEvent& event)
567 m_impl->frame->setWindowHasFocus(false);
572 void wxWebView::OnActivate(wxActivateEvent& event)
575 m_impl->frame->setIsActive(event.GetActive());