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