/*
* Copyright (C) 2006, 2007, 2008, 2009, 2010 Apple, Inc. All rights reserved.
* Copyright (C) 2010 Patrick Gansterer <paroga@paroga.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "WebView.h"
#include "ChromeClientWinCE.h"
#include "ContextMenuClientWinCE.h"
#include "DragClientWinCE.h"
#include "EditorClientWinCE.h"
#include "FocusController.h"
#include "Frame.h"
#include "FrameLoader.h"
#include "FrameLoaderClientWinCE.h"
#include "FrameView.h"
#include "GraphicsContext.h"
#include "InitializeThreading.h"
#include "InspectorClientWinCE.h"
#include "IntSize.h"
#include "MainThread.h"
#include "NotImplemented.h"
#include "Page.h"
#include "PlatformKeyboardEvent.h"
#include "PlatformMouseEvent.h"
#include "PlatformStrategiesWinCE.h"
#include "PlatformWheelEvent.h"
#include "ResourceRequest.h"
#include "Settings.h"
#include "SharedBuffer.h"
#include "WebCoreInstanceHandle.h"
using namespace WebCore;
const LPCWSTR kWebViewWindowClassName = L"WebViewWindowClass";
LRESULT CALLBACK WebView::webViewWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
if (WebView* webView = reinterpret_cast<WebView*>(GetWindowLong(hWnd, 0)))
return webView->wndProc(hWnd, message, wParam, lParam);
return DefWindowProc(hWnd, message, wParam, lParam);
}
PassRefPtr<SharedBuffer> loadResourceIntoBuffer(const char* name)
{
notImplemented();
return 0;
}
WebView::WebView(HWND hwnd, unsigned features)
: m_frame(0)
, m_page(0)
, m_parentWindowHandle(hwnd)
, m_enableDoubleBuffer(features & EnableDoubleBuffering)
{
RECT rcClient;
GetClientRect(hwnd, &rcClient);
m_windowHandle = CreateWindow(kWebViewWindowClassName, 0, WS_CHILD,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, hwnd, 0, WebCore::instanceHandle(), 0);
SetWindowLong(m_windowHandle, 0, reinterpret_cast<LONG>(this));
MoveWindow(m_windowHandle, 0, 0, rcClient.right, rcClient.bottom, TRUE);
ShowWindow(m_windowHandle, SW_SHOW);
Page::PageClients pageClients;
pageClients.chromeClient = new WebKit::ChromeClientWinCE(this);
pageClients.contextMenuClient = new WebKit::ContextMenuClientWinCE(this);
pageClients.editorClient = new WebKit::EditorClientWinCE(this);
pageClients.dragClient = new WebKit::DragClientWinCE();
pageClients.inspectorClient = new WebKit::InspectorClientWinCE(this);
m_page = new Page(pageClients);
Settings* settings = m_page->settings();
settings->setDefaultFixedFontSize(14);
settings->setDefaultFontSize(14);
settings->setMinimumFontSize(8);
settings->setMinimumLogicalFontSize(8);
settings->setJavaScriptEnabled(true);
settings->setLoadsImagesAutomatically(true);
WebKit::FrameLoaderClientWinCE* loaderClient = new WebKit::FrameLoaderClientWinCE(this);
RefPtr<Frame> frame = Frame::create(m_page, 0, loaderClient);
m_frame = frame.get();
loaderClient->setFrame(m_frame);
m_page->mainFrame()->init();
if (view()) {
RECT windowRect;
frameRect(&windowRect);
view()->resize(IntRect(windowRect).size());
}
}
WebView::~WebView()
{
delete m_page;
DestroyWindow(m_windowHandle);
}
void WebView::initialize(HINSTANCE instanceHandle)
{
JSC::initializeThreading();
WTF::initializeMainThread();
PlatformStrategiesWinCE::initialize();
WebCore::setInstanceHandle(instanceHandle);
WNDCLASS wc;
wc.style = CS_DBLCLKS;
wc.lpfnWndProc = WebView::webViewWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = sizeof(void *);
wc.hInstance = instanceHandle;
wc.hIcon = 0;
wc.hCursor = LoadCursor(0, IDC_ARROW);
wc.hbrBackground = 0;
wc.lpszMenuName = 0;
wc.lpszClassName = kWebViewWindowClassName;
RegisterClass(&wc);
}
void WebView::cleanup()
{
UnregisterClass(kWebViewWindowClassName, WebCore::instanceHandle());
}
PassRefPtr<Frame> WebView::createFrame(const KURL& url, const String& name, HTMLFrameOwnerElement* ownerElement, const String& referrer,
bool /*allowsScrolling*/, int /*marginWidth*/, int /*marginHeight*/)
{
Frame* coreFrame = m_frame;
WebKit::FrameLoaderClientWinCE *loaderClient = new WebKit::FrameLoaderClientWinCE(this);
RefPtr<Frame> childFrame = Frame::create(m_page, ownerElement, loaderClient);
loaderClient->setFrame(childFrame.get());
coreFrame->tree()->appendChild(childFrame);
childFrame->tree()->setName(name);
childFrame->init();
// The creation of the frame may have run arbitrary JavaScript that removed it from the page already.
if (!childFrame->page())
return 0;
coreFrame->loader()->loadURLIntoChildFrame(url, referrer, childFrame.get());
// The frame's onload handler may have removed it from the document.
if (!childFrame->tree()->parent())
return 0;
return childFrame.release();
}
void WebView::runJavaScriptAlert(const String& message)
{
notImplemented();
}
bool WebView::runJavaScriptConfirm(const String& message)
{
notImplemented();
return false;
}
bool WebView::runJavaScriptPrompt(const String& message, const String& defaultValue, String& result)
{
notImplemented();
return false;
}
void WebView::frameRect(RECT* rect) const
{
GetWindowRect(m_windowHandle, rect);
}
FrameView* WebView::view() const
{
return m_frame ? m_frame->view() : 0;
}
void WebView::load(LPCWSTR url)
{
load(String(url));
}
void WebView::load(const String &url)
{
load(WebCore::ResourceRequest(url));
}
void WebView::load(const WebCore::ResourceRequest &request)
{
frame()->loader()->load(request, false);
}
void WebView::reload()
{
frame()->loader()->reload();
}
void WebView::stop()
{
frame()->loader()->stopAllLoaders();
}
void WebView::paint(HDC hDC, const IntRect& clipRect)
{
FrameView* frameView = view();
if (!frameView)
return;
OwnPtr<HRGN> clipRgn(CreateRectRgn(clipRect.x(), clipRect.y(), clipRect.maxX(), clipRect.maxY()));
SelectClipRgn(hDC, clipRgn.get());
frameView->updateLayoutAndStyleIfNeededRecursive();
GraphicsContext gc(hDC);
frameView->paint(&gc, clipRect);
}
bool WebView::handlePaint(HWND hWnd)
{
RECT updateRect;
if (!GetUpdateRect(hWnd, &updateRect, false))
return false;
PAINTSTRUCT ps;
HDC hDC = BeginPaint(m_windowHandle, &ps);
IntRect clipRect(updateRect);
if (m_enableDoubleBuffer) {
if (!m_doubleBufferDC) {
RECT rcClient;
GetClientRect(m_windowHandle, &rcClient);
m_doubleBufferDC = adoptPtr(CreateCompatibleDC(hDC));
m_doubleBufferBitmap = adoptPtr(CreateCompatibleBitmap(hDC, rcClient.right, rcClient.bottom));
SelectObject(m_doubleBufferDC.get(), m_doubleBufferBitmap.get());
}
paint(m_doubleBufferDC.get(), clipRect);
BitBlt(hDC, clipRect.x(), clipRect.y(), clipRect.width(), clipRect.height(), m_doubleBufferDC.get(), clipRect.x(), clipRect.y(), SRCCOPY);
} else
paint(hDC, clipRect);
EndPaint(m_windowHandle, &ps);
return true;
}
bool WebView::handleMouseEvent(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static LONG globalClickCount;
static IntPoint globalPrevPoint;
static MouseButton globalPrevButton;
static LONG globalPrevMouseDownTime;
// Create our event.
// On WM_MOUSELEAVE we need to create a mouseout event, so we force the position
// of the event to be at (MINSHORT, MINSHORT).
PlatformMouseEvent mouseEvent(hWnd, message, wParam, lParam);
bool insideThreshold = abs(globalPrevPoint.x() - mouseEvent.pos().x()) < ::GetSystemMetrics(SM_CXDOUBLECLK)
&& abs(globalPrevPoint.y() - mouseEvent.pos().y()) < ::GetSystemMetrics(SM_CYDOUBLECLK);
LONG messageTime = 0;
bool handled = false;
if (message == WM_LBUTTONDOWN || message == WM_MBUTTONDOWN || message == WM_RBUTTONDOWN) {
// FIXME: I'm not sure if this is the "right" way to do this
// but without this call, we never become focused since we don't allow
// the default handling of mouse events.
SetFocus(m_windowHandle);
PlatformMouseEvent moveEvent(hWnd, WM_MOUSEMOVE, 0, lParam, false);
moveEvent.setClickCount(0);
m_page->mainFrame()->eventHandler()->handleMouseMoveEvent(moveEvent);
// Always start capturing events when the mouse goes down in our HWND.
SetCapture(m_windowHandle);
if (insideThreshold && mouseEvent.button() == globalPrevButton)
globalClickCount++;
else
// Reset the click count.
globalClickCount = 1;
globalPrevMouseDownTime = messageTime;
globalPrevButton = mouseEvent.button();
globalPrevPoint = mouseEvent.pos();
mouseEvent.setClickCount(globalClickCount);
handled = m_page->mainFrame()->eventHandler()->handleMousePressEvent(mouseEvent);
} else if (message == WM_LBUTTONDBLCLK || message == WM_MBUTTONDBLCLK || message == WM_RBUTTONDBLCLK) {
globalClickCount++;
mouseEvent.setClickCount(globalClickCount);
handled = m_page->mainFrame()->eventHandler()->handleMousePressEvent(mouseEvent);
} else if (message == WM_LBUTTONUP || message == WM_MBUTTONUP || message == WM_RBUTTONUP) {
// Record the global position and the button of the up.
globalPrevButton = mouseEvent.button();
globalPrevPoint = mouseEvent.pos();
mouseEvent.setClickCount(globalClickCount);
m_page->mainFrame()->eventHandler()->handleMouseReleaseEvent(mouseEvent);
ReleaseCapture();
} else if (message == WM_MOUSEMOVE) {
if (!insideThreshold)
globalClickCount = 0;
mouseEvent.setClickCount(globalClickCount);
handled = m_page->mainFrame()->eventHandler()->mouseMoved(mouseEvent);
}
return handled;
}
bool WebView::handleMouseWheel(HWND hWnd, WPARAM wParam, LPARAM lParam, bool isHorizontal)
{
PlatformWheelEvent wheelEvent(hWnd, wParam, lParam, isHorizontal);
return frame()->eventHandler()->handleWheelEvent(wheelEvent);
}
bool WebView::handleKeyDown(WPARAM virtualKeyCode, LPARAM keyData, bool systemKeyDown)
{
Frame* frame = m_page->focusController()->focusedOrMainFrame();
PlatformKeyboardEvent keyEvent(m_windowHandle, virtualKeyCode, keyData, PlatformKeyboardEvent::RawKeyDown, systemKeyDown);
bool handled = frame->eventHandler()->keyEvent(keyEvent);
// These events cannot be canceled, and we have no default handling for them.
// FIXME: match IE list more closely, see <http://msdn2.microsoft.com/en-us/library/ms536938.aspx>.
if (systemKeyDown && virtualKeyCode != VK_RETURN)
return false;
if (handled) {
MSG msg;
if (!systemKeyDown)
::PeekMessage(&msg, m_windowHandle, WM_CHAR, WM_CHAR, PM_REMOVE);
return true;
}
return handled;
}
bool WebView::handleKeyPress(WPARAM charCode, LPARAM keyData, bool systemKeyDown)
{
Frame* frame = m_page->focusController()->focusedOrMainFrame();
PlatformKeyboardEvent keyEvent(m_windowHandle, charCode, keyData, PlatformKeyboardEvent::Char, systemKeyDown);
// IE does not dispatch keypress event for WM_SYSCHAR.
if (systemKeyDown)
return frame->eventHandler()->handleAccessKey(keyEvent);
if (frame->eventHandler()->keyEvent(keyEvent))
return true;
return false;
}
bool WebView::handleKeyUp(WPARAM virtualKeyCode, LPARAM keyData, bool systemKeyDown)
{
PlatformKeyboardEvent keyEvent(m_windowHandle, virtualKeyCode, keyData, PlatformKeyboardEvent::KeyUp, systemKeyDown);
Frame* frame = m_page->focusController()->focusedOrMainFrame();
return frame->eventHandler()->keyEvent(keyEvent);
}
LRESULT WebView::wndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
bool handled = false;
if (view()) {
switch (message) {
case WM_PAINT:
handled = handlePaint(hWnd);
break;
case WM_MOUSEMOVE:
case WM_LBUTTONDOWN:
case WM_MBUTTONDOWN:
case WM_RBUTTONDOWN:
case WM_LBUTTONDBLCLK:
case WM_MBUTTONDBLCLK:
case WM_RBUTTONDBLCLK:
case WM_LBUTTONUP:
case WM_MBUTTONUP:
case WM_RBUTTONUP:
if (frame()->eventHandler() && view()->didFirstLayout())
handled = handleMouseEvent(hWnd, message, wParam, lParam);
break;
case WM_MOUSEWHEEL:
if (frame()->eventHandler() && view()->didFirstLayout())
handled = handleMouseWheel(hWnd, wParam, lParam, wParam & MK_SHIFT);
break;
case WM_SYSKEYDOWN:
handled = handleKeyDown(wParam, lParam, true);
break;
case WM_KEYDOWN:
handled = handleKeyDown(wParam, lParam, false);
break;
case WM_SYSKEYUP:
handled = handleKeyUp(wParam, lParam, true);
break;
case WM_KEYUP:
handled = handleKeyUp(wParam, lParam, false);
break;
case WM_SYSCHAR:
handled = handleKeyPress(wParam, lParam, true);
break;
case WM_CHAR:
handled = handleKeyPress(wParam, lParam, false);
break;
case WM_CLOSE:
PostMessage(m_parentWindowHandle, WM_CLOSE, wParam, lParam);
handled = true;
break;
default:
break;
}
}
if (handled)
return 0;
return DefWindowProc(hWnd, message, wParam, lParam);
}