/*
* Copyright (C) 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
*
* 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.
* 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* 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 "UIDelegate.h"
#include "DumpRenderTree.h"
#include "DraggingInfo.h"
#include "EventSender.h"
#include "LayoutTestController.h"
#include "DRTDesktopNotificationPresenter.h"
#include <WebCore/COMPtr.h>
#include <wtf/Platform.h>
#include <wtf/Vector.h>
#include <JavaScriptCore/Assertions.h>
#include <JavaScriptCore/JavaScriptCore.h>
#include <WebKit/WebKit.h>
#include <stdio.h>
using std::wstring;
class DRTUndoObject {
public:
DRTUndoObject(IWebUndoTarget* target, BSTR actionName, IUnknown* obj)
: m_target(target)
, m_actionName(SysAllocString(actionName))
, m_obj(obj)
{
}
~DRTUndoObject()
{
SysFreeString(m_actionName);
}
void invoke()
{
m_target->invoke(m_actionName, m_obj.get());
}
private:
IWebUndoTarget* m_target;
BSTR m_actionName;
COMPtr<IUnknown> m_obj;
};
class DRTUndoStack {
public:
~DRTUndoStack() { deleteAllValues(m_undoVector); }
bool isEmpty() const { return m_undoVector.isEmpty(); }
void clear() { deleteAllValues(m_undoVector); m_undoVector.clear(); }
void push(DRTUndoObject* undoObject) { m_undoVector.append(undoObject); }
DRTUndoObject* pop() { DRTUndoObject* top = m_undoVector.last(); m_undoVector.removeLast(); return top; }
private:
Vector<DRTUndoObject*> m_undoVector;
};
class DRTUndoManager {
public:
DRTUndoManager();
void removeAllActions();
void registerUndoWithTarget(IWebUndoTarget* target, BSTR actionName, IUnknown* obj);
void redo();
void undo();
bool canRedo() { return !m_redoStack->isEmpty(); }
bool canUndo() { return !m_undoStack->isEmpty(); }
private:
OwnPtr<DRTUndoStack> m_redoStack;
OwnPtr<DRTUndoStack> m_undoStack;
bool m_isRedoing;
bool m_isUndoing;
};
DRTUndoManager::DRTUndoManager()
: m_redoStack(new DRTUndoStack)
, m_undoStack(new DRTUndoStack)
, m_isRedoing(false)
, m_isUndoing(false)
{
}
void DRTUndoManager::removeAllActions()
{
m_redoStack->clear();
m_undoStack->clear();
}
void DRTUndoManager::registerUndoWithTarget(IWebUndoTarget* target, BSTR actionName, IUnknown* obj)
{
if (!m_isUndoing && !m_isRedoing)
m_redoStack->clear();
DRTUndoStack* stack = m_isUndoing ? m_redoStack.get() : m_undoStack.get();
stack->push(new DRTUndoObject(target, actionName, obj));
}
void DRTUndoManager::redo()
{
if (!canRedo())
return;
m_isRedoing = true;
DRTUndoObject* redoObject = m_redoStack->pop();
redoObject->invoke();
delete redoObject;
m_isRedoing = false;
}
void DRTUndoManager::undo()
{
if (!canUndo())
return;
m_isUndoing = true;
DRTUndoObject* undoObject = m_undoStack->pop();
undoObject->invoke();
delete undoObject;
m_isUndoing = false;
}
UIDelegate::UIDelegate()
: m_refCount(1)
, m_undoManager(new DRTUndoManager)
, m_desktopNotifications(new DRTDesktopNotificationPresenter)
{
m_frame.bottom = 0;
m_frame.top = 0;
m_frame.left = 0;
m_frame.right = 0;
}
void UIDelegate::resetUndoManager()
{
m_undoManager.set(new DRTUndoManager);
}
HRESULT STDMETHODCALLTYPE UIDelegate::QueryInterface(REFIID riid, void** ppvObject)
{
*ppvObject = 0;
if (IsEqualGUID(riid, IID_IUnknown))
*ppvObject = static_cast<IWebUIDelegate*>(this);
else if (IsEqualGUID(riid, IID_IWebUIDelegate))
*ppvObject = static_cast<IWebUIDelegate*>(this);
else if (IsEqualGUID(riid, IID_IWebUIDelegate2))
*ppvObject = static_cast<IWebUIDelegate2*>(this);
else if (IsEqualGUID(riid, IID_IWebUIDelegatePrivate))
*ppvObject = static_cast<IWebUIDelegatePrivate*>(this);
else if (IsEqualGUID(riid, IID_IWebUIDelegatePrivate2))
*ppvObject = static_cast<IWebUIDelegatePrivate2*>(this);
else if (IsEqualGUID(riid, IID_IWebUIDelegatePrivate3))
*ppvObject = static_cast<IWebUIDelegatePrivate3*>(this);
else
return E_NOINTERFACE;
AddRef();
return S_OK;
}
ULONG STDMETHODCALLTYPE UIDelegate::AddRef()
{
return ++m_refCount;
}
ULONG STDMETHODCALLTYPE UIDelegate::Release()
{
ULONG newRef = --m_refCount;
if (!newRef)
delete(this);
return newRef;
}
HRESULT STDMETHODCALLTYPE UIDelegate::hasCustomMenuImplementation(
/* [retval][out] */ BOOL *hasCustomMenus)
{
*hasCustomMenus = TRUE;
return S_OK;
}
HRESULT STDMETHODCALLTYPE UIDelegate::trackCustomPopupMenu(
/* [in] */ IWebView *sender,
/* [in] */ OLE_HANDLE menu,
/* [in] */ LPPOINT point)
{
// Do nothing
return S_OK;
}
HRESULT STDMETHODCALLTYPE UIDelegate::registerUndoWithTarget(
/* [in] */ IWebUndoTarget* target,
/* [in] */ BSTR actionName,
/* [in] */ IUnknown* actionArg)
{
m_undoManager->registerUndoWithTarget(target, actionName, actionArg);
return S_OK;
}
HRESULT STDMETHODCALLTYPE UIDelegate::removeAllActionsWithTarget(
/* [in] */ IWebUndoTarget*)
{
m_undoManager->removeAllActions();
return S_OK;
}
HRESULT STDMETHODCALLTYPE UIDelegate::setActionTitle(
/* [in] */ BSTR actionTitle)
{
// It is not neccessary to implement this for DRT because there is
// menu to write out the title to.
return S_OK;
}
HRESULT STDMETHODCALLTYPE UIDelegate::undo()
{
m_undoManager->undo();
return S_OK;
}
HRESULT STDMETHODCALLTYPE UIDelegate::redo()
{
m_undoManager->redo();
return S_OK;
}
HRESULT STDMETHODCALLTYPE UIDelegate::canUndo(
/* [retval][out] */ BOOL* result)
{
if (!result)
return E_POINTER;
*result = m_undoManager->canUndo();
return S_OK;
}
HRESULT STDMETHODCALLTYPE UIDelegate::canRedo(
/* [retval][out] */ BOOL* result)
{
if (!result)
return E_POINTER;
*result = m_undoManager->canRedo();
return S_OK;
}
HRESULT STDMETHODCALLTYPE UIDelegate::printFrame(
/* [in] */ IWebView *webView,
/* [in] */ IWebFrame *frame)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE UIDelegate::ftpDirectoryTemplatePath(
/* [in] */ IWebView *webView,
/* [retval][out] */ BSTR *path)
{
if (!path)
return E_POINTER;
*path = 0;
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE UIDelegate::webViewHeaderHeight(
/* [in] */ IWebView *webView,
/* [retval][out] */ float *result)
{
if (!result)
return E_POINTER;
*result = 0;
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE UIDelegate::webViewFooterHeight(
/* [in] */ IWebView *webView,
/* [retval][out] */ float *result)
{
if (!result)
return E_POINTER;
*result = 0;
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE UIDelegate::drawHeaderInRect(
/* [in] */ IWebView *webView,
/* [in] */ RECT *rect,
/* [in] */ OLE_HANDLE drawingContext)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE UIDelegate::drawFooterInRect(
/* [in] */ IWebView *webView,
/* [in] */ RECT *rect,
/* [in] */ OLE_HANDLE drawingContext,
/* [in] */ UINT pageIndex,
/* [in] */ UINT pageCount)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE UIDelegate::webViewPrintingMarginRect(
/* [in] */ IWebView *webView,
/* [retval][out] */ RECT *rect)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE UIDelegate::canRunModal(
/* [in] */ IWebView *webView,
/* [retval][out] */ BOOL *canRunBoolean)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE UIDelegate::createModalDialog(
/* [in] */ IWebView *sender,
/* [in] */ IWebURLRequest *request,
/* [retval][out] */ IWebView **newWebView)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE UIDelegate::runModal(
/* [in] */ IWebView *webView)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE UIDelegate::isMenuBarVisible(
/* [in] */ IWebView *webView,
/* [retval][out] */ BOOL *visible)
{
if (!visible)
return E_POINTER;
*visible = false;
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE UIDelegate::setMenuBarVisible(
/* [in] */ IWebView *webView,
/* [in] */ BOOL visible)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE UIDelegate::runDatabaseSizeLimitPrompt(
/* [in] */ IWebView *webView,
/* [in] */ BSTR displayName,
/* [in] */ IWebFrame *initiatedByFrame,
/* [retval][out] */ BOOL *allowed)
{
if (!allowed)
return E_POINTER;
*allowed = false;
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE UIDelegate::paintCustomScrollbar(
/* [in] */ IWebView *webView,
/* [in] */ HDC hDC,
/* [in] */ RECT rect,
/* [in] */ WebScrollBarControlSize size,
/* [in] */ WebScrollbarControlState state,
/* [in] */ WebScrollbarControlPart pressedPart,
/* [in] */ BOOL vertical,
/* [in] */ float value,
/* [in] */ float proportion,
/* [in] */ WebScrollbarControlPartMask parts)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE UIDelegate::paintCustomScrollCorner(
/* [in] */ IWebView *webView,
/* [in] */ HDC hDC,
/* [in] */ RECT rect)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE UIDelegate::setFrame(
/* [in] */ IWebView* /*sender*/,
/* [in] */ RECT* frame)
{
m_frame = *frame;
return S_OK;
}
HRESULT STDMETHODCALLTYPE UIDelegate::webViewFrame(
/* [in] */ IWebView* /*sender*/,
/* [retval][out] */ RECT* frame)
{
*frame = m_frame;
return S_OK;
}
HRESULT STDMETHODCALLTYPE UIDelegate::runJavaScriptAlertPanelWithMessage(
/* [in] */ IWebView* /*sender*/,
/* [in] */ BSTR message)
{
printf("ALERT: %S\n", message ? message : L"");
return S_OK;
}
HRESULT STDMETHODCALLTYPE UIDelegate::runJavaScriptConfirmPanelWithMessage(
/* [in] */ IWebView* sender,
/* [in] */ BSTR message,
/* [retval][out] */ BOOL* result)
{
printf("CONFIRM: %S\n", message ? message : L"");
*result = TRUE;
return S_OK;
}
HRESULT STDMETHODCALLTYPE UIDelegate::runJavaScriptTextInputPanelWithPrompt(
/* [in] */ IWebView *sender,
/* [in] */ BSTR message,
/* [in] */ BSTR defaultText,
/* [retval][out] */ BSTR *result)
{
printf("PROMPT: %S, default text: %S\n", message ? message : L"", defaultText ? defaultText : L"");
*result = SysAllocString(defaultText);
return S_OK;
}
HRESULT STDMETHODCALLTYPE UIDelegate::runBeforeUnloadConfirmPanelWithMessage(
/* [in] */ IWebView* /*sender*/,
/* [in] */ BSTR /*message*/,
/* [in] */ IWebFrame* /*initiatedByFrame*/,
/* [retval][out] */ BOOL* result)
{
if (!result)
return E_POINTER;
*result = TRUE;
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE UIDelegate::webViewAddMessageToConsole(
/* [in] */ IWebView* sender,
/* [in] */ BSTR message,
/* [in] */ int lineNumber,
/* [in] */ BSTR url,
/* [in] */ BOOL isError)
{
wstring newMessage;
if (message) {
newMessage = message;
size_t fileProtocol = newMessage.find(L"file://");
if (fileProtocol != wstring::npos)
newMessage = newMessage.substr(0, fileProtocol) + lastPathComponent(newMessage.substr(fileProtocol));
}
printf("CONSOLE MESSAGE: line %d: %s\n", lineNumber, toUTF8(newMessage).c_str());
return S_OK;
}
HRESULT STDMETHODCALLTYPE UIDelegate::doDragDrop(
/* [in] */ IWebView* sender,
/* [in] */ IDataObject* object,
/* [in] */ IDropSource* source,
/* [in] */ DWORD okEffect,
/* [retval][out] */ DWORD* performedEffect)
{
if (!performedEffect)
return E_POINTER;
*performedEffect = 0;
draggingInfo = new DraggingInfo(object, source);
HRESULT oleDragAndDropReturnValue = DRAGDROP_S_CANCEL;
replaySavedEvents(&oleDragAndDropReturnValue);
if (draggingInfo) {
*performedEffect = draggingInfo->performedDropEffect();
delete draggingInfo;
draggingInfo = 0;
}
return oleDragAndDropReturnValue;
}
HRESULT STDMETHODCALLTYPE UIDelegate::webViewGetDlgCode(
/* [in] */ IWebView* /*sender*/,
/* [in] */ UINT /*keyCode*/,
/* [retval][out] */ LONG_PTR *code)
{
if (!code)
return E_POINTER;
*code = 0;
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE UIDelegate::createWebViewWithRequest(
/* [in] */ IWebView *sender,
/* [in] */ IWebURLRequest *request,
/* [retval][out] */ IWebView **newWebView)
{
if (!::gLayoutTestController->canOpenWindows())
return E_FAIL;
*newWebView = createWebViewAndOffscreenWindow();
return S_OK;
}
HRESULT STDMETHODCALLTYPE UIDelegate::webViewClose(
/* [in] */ IWebView *sender)
{
HWND hostWindow;
sender->hostWindow(reinterpret_cast<OLE_HANDLE*>(&hostWindow));
DestroyWindow(hostWindow);
return S_OK;
}
HRESULT STDMETHODCALLTYPE UIDelegate::webViewFocus(
/* [in] */ IWebView *sender)
{
HWND hostWindow;
sender->hostWindow(reinterpret_cast<OLE_HANDLE*>(&hostWindow));
SetForegroundWindow(hostWindow);
return S_OK;
}
HRESULT STDMETHODCALLTYPE UIDelegate::webViewUnfocus(
/* [in] */ IWebView *sender)
{
SetForegroundWindow(GetDesktopWindow());
return S_OK;
}
HRESULT STDMETHODCALLTYPE UIDelegate::webViewPainted(
/* [in] */ IWebView *sender)
{
return S_OK;
}
HRESULT STDMETHODCALLTYPE UIDelegate::exceededDatabaseQuota(
/* [in] */ IWebView *sender,
/* [in] */ IWebFrame *frame,
/* [in] */ IWebSecurityOrigin *origin,
/* [in] */ BSTR databaseIdentifier)
{
BSTR protocol;
BSTR host;
unsigned short port;
origin->protocol(&protocol);
origin->host(&host);
origin->port(&port);
if (!done && gLayoutTestController->dumpDatabaseCallbacks())
printf("UI DELEGATE DATABASE CALLBACK: exceededDatabaseQuotaForSecurityOrigin:{%S, %S, %i} database:%S\n", protocol, host, port, databaseIdentifier);
SysFreeString(protocol);
SysFreeString(host);
static const unsigned long long defaultQuota = 5 * 1024 * 1024;
origin->setQuota(defaultQuota);
return S_OK;
}
HRESULT STDMETHODCALLTYPE UIDelegate::embeddedViewWithArguments(
/* [in] */ IWebView *sender,
/* [in] */ IWebFrame *frame,
/* [in] */ IPropertyBag *arguments,
/* [retval][out] */ IWebEmbeddedView **view)
{
if (!view)
return E_POINTER;
*view = 0;
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE UIDelegate::webViewClosing(
/* [in] */ IWebView *sender)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE UIDelegate::webViewSetCursor(
/* [in] */ IWebView *sender,
/* [in] */ OLE_HANDLE cursor)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE UIDelegate::webViewDidInvalidate(
/* [in] */ IWebView *sender)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE UIDelegate::setStatusText(IWebView*, BSTR text)
{
if (gLayoutTestController->dumpStatusCallbacks())
printf("UI DELEGATE STATUS CALLBACK: setStatusText:%s\n", text ? toUTF8(text).c_str() : "");
return S_OK;
}
HRESULT STDMETHODCALLTYPE UIDelegate::desktopNotificationsDelegate(IWebDesktopNotificationsDelegate** result)
{
m_desktopNotifications.copyRefTo(result);
return S_OK;
}
HRESULT STDMETHODCALLTYPE UIDelegate::createWebViewWithRequest(IWebView* sender, IWebURLRequest* request, IPropertyBag* windowFeatures, IWebView** newWebView)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE UIDelegate::drawBackground(IWebView* sender, OLE_HANDLE hdc, const RECT* dirtyRect)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE UIDelegate::decidePolicyForGeolocationRequest(IWebView* sender, IWebFrame* frame, IWebSecurityOrigin* origin, IWebGeolocationPolicyListener* listener)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE UIDelegate::didPressMissingPluginButton(IDOMElement* element)
{
printf("MISSING PLUGIN BUTTON PRESSED\n");
return S_OK;
}