1/*
2 * Copyright (C) 2006, 2007, 2008, 2009, 2010 Apple, Inc.  All rights reserved.
3 * Copyright (C) 2010 Patrick Gansterer <paroga@paroga.com>
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
18 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "WebView.h"
28
29#include "ChromeClientWinCE.h"
30#include "ContextMenuClientWinCE.h"
31#include "DragClientWinCE.h"
32#include "EditorClientWinCE.h"
33#include "FocusController.h"
34#include "Frame.h"
35#include "FrameLoader.h"
36#include "FrameLoaderClientWinCE.h"
37#include "FrameView.h"
38#include "GraphicsContext.h"
39#include "InitializeThreading.h"
40#include "InspectorClientWinCE.h"
41#include "IntSize.h"
42#include "MainThread.h"
43#include "NotImplemented.h"
44#include "Page.h"
45#include "PlatformKeyboardEvent.h"
46#include "PlatformMouseEvent.h"
47#include "PlatformStrategiesWinCE.h"
48#include "PlatformWheelEvent.h"
49#include "ResourceRequest.h"
50#include "Settings.h"
51#include "SharedBuffer.h"
52#include "WebCoreInstanceHandle.h"
53
54using namespace WebCore;
55
56const LPCWSTR kWebViewWindowClassName = L"WebViewWindowClass";
57
58
59LRESULT CALLBACK WebView::webViewWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
60{
61    if (WebView* webView = reinterpret_cast<WebView*>(GetWindowLong(hWnd, 0)))
62        return webView->wndProc(hWnd, message, wParam, lParam);
63
64    return DefWindowProc(hWnd, message, wParam, lParam);
65}
66
67PassRefPtr<SharedBuffer> loadResourceIntoBuffer(const char* name)
68{
69    notImplemented();
70    return 0;
71}
72
73
74WebView::WebView(HWND hwnd, unsigned features)
75    : m_frame(0)
76    , m_page(0)
77    , m_parentWindowHandle(hwnd)
78    , m_enableDoubleBuffer(features & EnableDoubleBuffering)
79{
80    RECT rcClient;
81    GetClientRect(hwnd, &rcClient);
82
83    m_windowHandle = CreateWindow(kWebViewWindowClassName, 0, WS_CHILD,
84        CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, hwnd, 0, WebCore::instanceHandle(), 0);
85
86    SetWindowLong(m_windowHandle, 0, reinterpret_cast<LONG>(this));
87
88    MoveWindow(m_windowHandle, 0, 0, rcClient.right, rcClient.bottom, TRUE);
89    ShowWindow(m_windowHandle, SW_SHOW);
90
91    Page::PageClients pageClients;
92    pageClients.chromeClient = new WebKit::ChromeClientWinCE(this);
93    pageClients.contextMenuClient = new WebKit::ContextMenuClientWinCE(this);
94    pageClients.editorClient = new WebKit::EditorClientWinCE(this);
95    pageClients.dragClient = new WebKit::DragClientWinCE();
96    pageClients.inspectorClient = new WebKit::InspectorClientWinCE(this);
97    m_page = new Page(pageClients);
98
99    Settings* settings = m_page->settings();
100    settings->setDefaultFixedFontSize(14);
101    settings->setDefaultFontSize(14);
102    settings->setMinimumFontSize(8);
103    settings->setMinimumLogicalFontSize(8);
104    settings->setJavaScriptEnabled(true);
105    settings->setLoadsImagesAutomatically(true);
106
107    WebKit::FrameLoaderClientWinCE* loaderClient = new WebKit::FrameLoaderClientWinCE(this);
108    RefPtr<Frame> frame = Frame::create(m_page, 0, loaderClient);
109    m_frame = frame.get();
110    loaderClient->setFrame(m_frame);
111
112    m_page->mainFrame()->init();
113
114    if (view()) {
115        RECT windowRect;
116        frameRect(&windowRect);
117        view()->resize(IntRect(windowRect).size());
118    }
119}
120
121WebView::~WebView()
122{
123    delete m_page;
124    DestroyWindow(m_windowHandle);
125}
126
127void WebView::initialize(HINSTANCE instanceHandle)
128{
129    JSC::initializeThreading();
130    WTF::initializeMainThread();
131    PlatformStrategiesWinCE::initialize();
132
133    WebCore::setInstanceHandle(instanceHandle);
134
135    WNDCLASS wc;
136    wc.style          = CS_DBLCLKS;
137    wc.lpfnWndProc    = WebView::webViewWndProc;
138    wc.cbClsExtra     = 0;
139    wc.cbWndExtra     = sizeof(void *);
140    wc.hInstance      = instanceHandle;
141    wc.hIcon          = 0;
142    wc.hCursor        = LoadCursor(0, IDC_ARROW);
143    wc.hbrBackground  = 0;
144    wc.lpszMenuName   = 0;
145    wc.lpszClassName  = kWebViewWindowClassName;
146
147    RegisterClass(&wc);
148}
149
150void WebView::cleanup()
151{
152    UnregisterClass(kWebViewWindowClassName, WebCore::instanceHandle());
153}
154
155PassRefPtr<Frame> WebView::createFrame(const KURL& url, const String& name, HTMLFrameOwnerElement* ownerElement, const String& referrer,
156                                       bool /*allowsScrolling*/, int /*marginWidth*/, int /*marginHeight*/)
157{
158    Frame* coreFrame = m_frame;
159
160    WebKit::FrameLoaderClientWinCE *loaderClient = new WebKit::FrameLoaderClientWinCE(this);
161    RefPtr<Frame> childFrame = Frame::create(m_page, ownerElement, loaderClient);
162    loaderClient->setFrame(childFrame.get());
163
164    coreFrame->tree()->appendChild(childFrame);
165    childFrame->tree()->setName(name);
166    childFrame->init();
167
168    // The creation of the frame may have run arbitrary JavaScript that removed it from the page already.
169    if (!childFrame->page())
170        return 0;
171
172    coreFrame->loader()->loadURLIntoChildFrame(url, referrer, childFrame.get());
173
174    // The frame's onload handler may have removed it from the document.
175    if (!childFrame->tree()->parent())
176        return 0;
177
178    return childFrame.release();
179}
180
181void WebView::runJavaScriptAlert(const String& message)
182{
183    notImplemented();
184}
185
186bool WebView::runJavaScriptConfirm(const String& message)
187{
188    notImplemented();
189    return false;
190}
191
192bool WebView::runJavaScriptPrompt(const String& message, const String& defaultValue, String& result)
193{
194    notImplemented();
195    return false;
196}
197
198void WebView::frameRect(RECT* rect) const
199{
200    GetWindowRect(m_windowHandle, rect);
201}
202
203FrameView* WebView::view() const
204{
205    return m_frame ? m_frame->view() : 0;
206}
207
208void WebView::load(LPCWSTR url)
209{
210    load(String(url));
211}
212
213void WebView::load(const String &url)
214{
215    load(WebCore::ResourceRequest(url));
216}
217
218void WebView::load(const WebCore::ResourceRequest &request)
219{
220    frame()->loader()->load(request, false);
221}
222
223void WebView::reload()
224{
225    frame()->loader()->reload();
226}
227
228void WebView::stop()
229{
230    frame()->loader()->stopAllLoaders();
231}
232
233void WebView::paint(HDC hDC, const IntRect& clipRect)
234{
235    FrameView* frameView = view();
236    if (!frameView)
237        return;
238
239    OwnPtr<HRGN> clipRgn(CreateRectRgn(clipRect.x(), clipRect.y(), clipRect.maxX(), clipRect.maxY()));
240    SelectClipRgn(hDC, clipRgn.get());
241
242    frameView->updateLayoutAndStyleIfNeededRecursive();
243
244    GraphicsContext gc(hDC);
245    frameView->paint(&gc, clipRect);
246}
247
248bool WebView::handlePaint(HWND hWnd)
249{
250    RECT updateRect;
251    if (!GetUpdateRect(hWnd, &updateRect, false))
252        return false;
253
254    PAINTSTRUCT ps;
255    HDC hDC = BeginPaint(m_windowHandle, &ps);
256
257    IntRect clipRect(updateRect);
258
259    if (m_enableDoubleBuffer) {
260        if (!m_doubleBufferDC) {
261            RECT rcClient;
262            GetClientRect(m_windowHandle, &rcClient);
263
264            m_doubleBufferDC = adoptPtr(CreateCompatibleDC(hDC));
265            m_doubleBufferBitmap = adoptPtr(CreateCompatibleBitmap(hDC, rcClient.right, rcClient.bottom));
266            SelectObject(m_doubleBufferDC.get(), m_doubleBufferBitmap.get());
267        }
268
269        paint(m_doubleBufferDC.get(), clipRect);
270
271        BitBlt(hDC, clipRect.x(), clipRect.y(), clipRect.width(), clipRect.height(), m_doubleBufferDC.get(), clipRect.x(), clipRect.y(), SRCCOPY);
272    } else
273        paint(hDC, clipRect);
274
275    EndPaint(m_windowHandle, &ps);
276    return true;
277}
278
279bool WebView::handleMouseEvent(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
280{
281    static LONG globalClickCount;
282    static IntPoint globalPrevPoint;
283    static MouseButton globalPrevButton;
284    static LONG globalPrevMouseDownTime;
285
286    // Create our event.
287    // On WM_MOUSELEAVE we need to create a mouseout event, so we force the position
288    // of the event to be at (MINSHORT, MINSHORT).
289    PlatformMouseEvent mouseEvent(hWnd, message, wParam, lParam);
290
291    bool insideThreshold = abs(globalPrevPoint.x() - mouseEvent.pos().x()) < ::GetSystemMetrics(SM_CXDOUBLECLK)
292                           && abs(globalPrevPoint.y() - mouseEvent.pos().y()) < ::GetSystemMetrics(SM_CYDOUBLECLK);
293    LONG messageTime = 0;
294
295    bool handled = false;
296    if (message == WM_LBUTTONDOWN || message == WM_MBUTTONDOWN || message == WM_RBUTTONDOWN) {
297        // FIXME: I'm not sure if this is the "right" way to do this
298        // but without this call, we never become focused since we don't allow
299        // the default handling of mouse events.
300        SetFocus(m_windowHandle);
301
302        PlatformMouseEvent moveEvent(hWnd, WM_MOUSEMOVE, 0, lParam, false);
303        moveEvent.setClickCount(0);
304        m_page->mainFrame()->eventHandler()->handleMouseMoveEvent(moveEvent);
305
306        // Always start capturing events when the mouse goes down in our HWND.
307        SetCapture(m_windowHandle);
308
309        if (insideThreshold && mouseEvent.button() == globalPrevButton)
310            globalClickCount++;
311        else
312            // Reset the click count.
313            globalClickCount = 1;
314        globalPrevMouseDownTime = messageTime;
315        globalPrevButton = mouseEvent.button();
316        globalPrevPoint = mouseEvent.pos();
317
318        mouseEvent.setClickCount(globalClickCount);
319        handled = m_page->mainFrame()->eventHandler()->handleMousePressEvent(mouseEvent);
320    } else if (message == WM_LBUTTONDBLCLK || message == WM_MBUTTONDBLCLK || message == WM_RBUTTONDBLCLK) {
321        globalClickCount++;
322        mouseEvent.setClickCount(globalClickCount);
323        handled = m_page->mainFrame()->eventHandler()->handleMousePressEvent(mouseEvent);
324    } else if (message == WM_LBUTTONUP || message == WM_MBUTTONUP || message == WM_RBUTTONUP) {
325        // Record the global position and the button of the up.
326        globalPrevButton = mouseEvent.button();
327        globalPrevPoint = mouseEvent.pos();
328        mouseEvent.setClickCount(globalClickCount);
329        m_page->mainFrame()->eventHandler()->handleMouseReleaseEvent(mouseEvent);
330        ReleaseCapture();
331    } else if (message == WM_MOUSEMOVE) {
332        if (!insideThreshold)
333            globalClickCount = 0;
334        mouseEvent.setClickCount(globalClickCount);
335        handled = m_page->mainFrame()->eventHandler()->mouseMoved(mouseEvent);
336    }
337
338    return handled;
339}
340
341bool WebView::handleMouseWheel(HWND hWnd, WPARAM wParam, LPARAM lParam, bool isHorizontal)
342{
343    PlatformWheelEvent wheelEvent(hWnd, wParam, lParam, isHorizontal);
344    return frame()->eventHandler()->handleWheelEvent(wheelEvent);
345}
346
347bool WebView::handleKeyDown(WPARAM virtualKeyCode, LPARAM keyData, bool systemKeyDown)
348{
349    Frame* frame = m_page->focusController()->focusedOrMainFrame();
350
351    PlatformKeyboardEvent keyEvent(m_windowHandle, virtualKeyCode, keyData, PlatformKeyboardEvent::RawKeyDown, systemKeyDown);
352    bool handled = frame->eventHandler()->keyEvent(keyEvent);
353
354    // These events cannot be canceled, and we have no default handling for them.
355    // FIXME: match IE list more closely, see <http://msdn2.microsoft.com/en-us/library/ms536938.aspx>.
356    if (systemKeyDown && virtualKeyCode != VK_RETURN)
357        return false;
358
359    if (handled) {
360        MSG msg;
361        if (!systemKeyDown)
362            ::PeekMessage(&msg, m_windowHandle, WM_CHAR, WM_CHAR, PM_REMOVE);
363        return true;
364    }
365
366    return handled;
367}
368
369bool WebView::handleKeyPress(WPARAM charCode, LPARAM keyData, bool systemKeyDown)
370{
371    Frame* frame = m_page->focusController()->focusedOrMainFrame();
372
373    PlatformKeyboardEvent keyEvent(m_windowHandle, charCode, keyData, PlatformKeyboardEvent::Char, systemKeyDown);
374    // IE does not dispatch keypress event for WM_SYSCHAR.
375    if (systemKeyDown)
376        return frame->eventHandler()->handleAccessKey(keyEvent);
377    if (frame->eventHandler()->keyEvent(keyEvent))
378        return true;
379
380    return false;
381}
382
383bool WebView::handleKeyUp(WPARAM virtualKeyCode, LPARAM keyData, bool systemKeyDown)
384{
385    PlatformKeyboardEvent keyEvent(m_windowHandle, virtualKeyCode, keyData, PlatformKeyboardEvent::KeyUp, systemKeyDown);
386
387    Frame* frame = m_page->focusController()->focusedOrMainFrame();
388    return frame->eventHandler()->keyEvent(keyEvent);
389}
390
391LRESULT WebView::wndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
392{
393    bool handled = false;
394
395    if (view()) {
396        switch (message) {
397        case WM_PAINT:
398            handled = handlePaint(hWnd);
399            break;
400
401        case WM_MOUSEMOVE:
402        case WM_LBUTTONDOWN:
403        case WM_MBUTTONDOWN:
404        case WM_RBUTTONDOWN:
405        case WM_LBUTTONDBLCLK:
406        case WM_MBUTTONDBLCLK:
407        case WM_RBUTTONDBLCLK:
408        case WM_LBUTTONUP:
409        case WM_MBUTTONUP:
410        case WM_RBUTTONUP:
411            if (frame()->eventHandler() && view()->didFirstLayout())
412                handled = handleMouseEvent(hWnd, message, wParam, lParam);
413            break;
414
415        case WM_MOUSEWHEEL:
416            if (frame()->eventHandler() && view()->didFirstLayout())
417                handled = handleMouseWheel(hWnd, wParam, lParam, wParam & MK_SHIFT);
418            break;
419
420        case WM_SYSKEYDOWN:
421            handled = handleKeyDown(wParam, lParam, true);
422            break;
423
424        case WM_KEYDOWN:
425            handled = handleKeyDown(wParam, lParam, false);
426            break;
427
428        case WM_SYSKEYUP:
429            handled = handleKeyUp(wParam, lParam, true);
430            break;
431
432        case WM_KEYUP:
433            handled = handleKeyUp(wParam, lParam, false);
434            break;
435
436        case WM_SYSCHAR:
437            handled = handleKeyPress(wParam, lParam, true);
438            break;
439
440        case WM_CHAR:
441            handled = handleKeyPress(wParam, lParam, false);
442            break;
443
444        case WM_CLOSE:
445            PostMessage(m_parentWindowHandle, WM_CLOSE, wParam, lParam);
446            handled = true;
447            break;
448
449        default:
450            break;
451        }
452    }
453
454    if (handled)
455        return 0;
456
457    return DefWindowProc(hWnd, message, wParam, lParam);
458}
459