1/*
2 * Copyright (C) 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
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 * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14 *     its contributors may be used to endorse or promote products derived
15 *     from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include "config.h"
30#include "UIDelegate.h"
31
32#include "DumpRenderTree.h"
33#include "DraggingInfo.h"
34#include "EventSender.h"
35#include "LayoutTestController.h"
36#include "DRTDesktopNotificationPresenter.h"
37
38#include <WebCore/COMPtr.h>
39#include <wtf/Platform.h>
40#include <wtf/Vector.h>
41#include <JavaScriptCore/Assertions.h>
42#include <JavaScriptCore/JavaScriptCore.h>
43#include <WebKit/WebKit.h>
44#include <stdio.h>
45
46using std::wstring;
47
48class DRTUndoObject {
49public:
50    DRTUndoObject(IWebUndoTarget* target, BSTR actionName, IUnknown* obj)
51        : m_target(target)
52        , m_actionName(SysAllocString(actionName))
53        , m_obj(obj)
54    {
55    }
56
57    ~DRTUndoObject()
58    {
59        SysFreeString(m_actionName);
60    }
61
62    void invoke()
63    {
64        m_target->invoke(m_actionName, m_obj.get());
65    }
66
67private:
68    IWebUndoTarget* m_target;
69    BSTR m_actionName;
70    COMPtr<IUnknown> m_obj;
71};
72
73class DRTUndoStack {
74public:
75    ~DRTUndoStack() { deleteAllValues(m_undoVector); }
76
77    bool isEmpty() const { return m_undoVector.isEmpty(); }
78    void clear() { deleteAllValues(m_undoVector); m_undoVector.clear(); }
79
80    void push(DRTUndoObject* undoObject) { m_undoVector.append(undoObject); }
81    DRTUndoObject* pop() { DRTUndoObject* top = m_undoVector.last(); m_undoVector.removeLast(); return top; }
82
83private:
84    Vector<DRTUndoObject*> m_undoVector;
85};
86
87class DRTUndoManager {
88public:
89    DRTUndoManager();
90
91    void removeAllActions();
92    void registerUndoWithTarget(IWebUndoTarget* target, BSTR actionName, IUnknown* obj);
93    void redo();
94    void undo();
95    bool canRedo() { return !m_redoStack->isEmpty(); }
96    bool canUndo() { return !m_undoStack->isEmpty(); }
97
98private:
99    OwnPtr<DRTUndoStack> m_redoStack;
100    OwnPtr<DRTUndoStack> m_undoStack;
101    bool m_isRedoing;
102    bool m_isUndoing;
103};
104
105DRTUndoManager::DRTUndoManager()
106    : m_redoStack(new DRTUndoStack)
107    , m_undoStack(new DRTUndoStack)
108    , m_isRedoing(false)
109    , m_isUndoing(false)
110{
111}
112
113void DRTUndoManager::removeAllActions()
114{
115    m_redoStack->clear();
116    m_undoStack->clear();
117}
118
119void DRTUndoManager::registerUndoWithTarget(IWebUndoTarget* target, BSTR actionName, IUnknown* obj)
120{
121    if (!m_isUndoing && !m_isRedoing)
122        m_redoStack->clear();
123
124    DRTUndoStack* stack = m_isUndoing ? m_redoStack.get() : m_undoStack.get();
125    stack->push(new DRTUndoObject(target, actionName, obj));
126}
127
128void DRTUndoManager::redo()
129{
130    if (!canRedo())
131        return;
132
133    m_isRedoing = true;
134
135    DRTUndoObject* redoObject = m_redoStack->pop();
136    redoObject->invoke();
137    delete redoObject;
138
139    m_isRedoing = false;
140}
141
142void DRTUndoManager::undo()
143{
144    if (!canUndo())
145        return;
146
147    m_isUndoing = true;
148
149    DRTUndoObject* undoObject = m_undoStack->pop();
150    undoObject->invoke();
151    delete undoObject;
152
153    m_isUndoing = false;
154}
155
156UIDelegate::UIDelegate()
157    : m_refCount(1)
158    , m_undoManager(new DRTUndoManager)
159    , m_desktopNotifications(new DRTDesktopNotificationPresenter)
160{
161    m_frame.bottom = 0;
162    m_frame.top = 0;
163    m_frame.left = 0;
164    m_frame.right = 0;
165}
166
167void UIDelegate::resetUndoManager()
168{
169    m_undoManager.set(new DRTUndoManager);
170}
171
172HRESULT STDMETHODCALLTYPE UIDelegate::QueryInterface(REFIID riid, void** ppvObject)
173{
174    *ppvObject = 0;
175    if (IsEqualGUID(riid, IID_IUnknown))
176        *ppvObject = static_cast<IWebUIDelegate*>(this);
177    else if (IsEqualGUID(riid, IID_IWebUIDelegate))
178        *ppvObject = static_cast<IWebUIDelegate*>(this);
179    else if (IsEqualGUID(riid, IID_IWebUIDelegate2))
180        *ppvObject = static_cast<IWebUIDelegate2*>(this);
181    else if (IsEqualGUID(riid, IID_IWebUIDelegatePrivate))
182        *ppvObject = static_cast<IWebUIDelegatePrivate*>(this);
183    else if (IsEqualGUID(riid, IID_IWebUIDelegatePrivate2))
184        *ppvObject = static_cast<IWebUIDelegatePrivate2*>(this);
185    else if (IsEqualGUID(riid, IID_IWebUIDelegatePrivate3))
186        *ppvObject = static_cast<IWebUIDelegatePrivate3*>(this);
187    else
188        return E_NOINTERFACE;
189
190    AddRef();
191    return S_OK;
192}
193
194ULONG STDMETHODCALLTYPE UIDelegate::AddRef()
195{
196    return ++m_refCount;
197}
198
199ULONG STDMETHODCALLTYPE UIDelegate::Release()
200{
201    ULONG newRef = --m_refCount;
202    if (!newRef)
203        delete(this);
204
205    return newRef;
206}
207
208HRESULT STDMETHODCALLTYPE UIDelegate::hasCustomMenuImplementation(
209        /* [retval][out] */ BOOL *hasCustomMenus)
210{
211    *hasCustomMenus = TRUE;
212
213    return S_OK;
214}
215
216HRESULT STDMETHODCALLTYPE UIDelegate::trackCustomPopupMenu(
217        /* [in] */ IWebView *sender,
218        /* [in] */ OLE_HANDLE menu,
219        /* [in] */ LPPOINT point)
220{
221    // Do nothing
222    return S_OK;
223}
224
225HRESULT STDMETHODCALLTYPE UIDelegate::registerUndoWithTarget(
226        /* [in] */ IWebUndoTarget* target,
227        /* [in] */ BSTR actionName,
228        /* [in] */ IUnknown* actionArg)
229{
230    m_undoManager->registerUndoWithTarget(target, actionName, actionArg);
231    return S_OK;
232}
233
234HRESULT STDMETHODCALLTYPE UIDelegate::removeAllActionsWithTarget(
235        /* [in] */ IWebUndoTarget*)
236{
237    m_undoManager->removeAllActions();
238    return S_OK;
239}
240
241HRESULT STDMETHODCALLTYPE UIDelegate::setActionTitle(
242        /* [in] */ BSTR actionTitle)
243{
244    // It is not neccessary to implement this for DRT because there is
245    // menu to write out the title to.
246    return S_OK;
247}
248
249HRESULT STDMETHODCALLTYPE UIDelegate::undo()
250{
251    m_undoManager->undo();
252    return S_OK;
253}
254
255HRESULT STDMETHODCALLTYPE UIDelegate::redo()
256{
257    m_undoManager->redo();
258    return S_OK;
259}
260
261HRESULT STDMETHODCALLTYPE UIDelegate::canUndo(
262        /* [retval][out] */ BOOL* result)
263{
264    if (!result)
265        return E_POINTER;
266
267    *result = m_undoManager->canUndo();
268    return S_OK;
269}
270
271HRESULT STDMETHODCALLTYPE UIDelegate::canRedo(
272        /* [retval][out] */ BOOL* result)
273{
274    if (!result)
275        return E_POINTER;
276
277    *result = m_undoManager->canRedo();
278    return S_OK;
279}
280
281HRESULT STDMETHODCALLTYPE UIDelegate::printFrame(
282    /* [in] */ IWebView *webView,
283    /* [in] */ IWebFrame *frame)
284{
285    return E_NOTIMPL;
286}
287
288HRESULT STDMETHODCALLTYPE UIDelegate::ftpDirectoryTemplatePath(
289    /* [in] */ IWebView *webView,
290    /* [retval][out] */ BSTR *path)
291{
292    if (!path)
293        return E_POINTER;
294    *path = 0;
295    return E_NOTIMPL;
296}
297
298
299HRESULT STDMETHODCALLTYPE UIDelegate::webViewHeaderHeight(
300    /* [in] */ IWebView *webView,
301    /* [retval][out] */ float *result)
302{
303    if (!result)
304        return E_POINTER;
305    *result = 0;
306    return E_NOTIMPL;
307}
308
309HRESULT STDMETHODCALLTYPE UIDelegate::webViewFooterHeight(
310    /* [in] */ IWebView *webView,
311    /* [retval][out] */ float *result)
312{
313    if (!result)
314        return E_POINTER;
315    *result = 0;
316    return E_NOTIMPL;
317}
318
319HRESULT STDMETHODCALLTYPE UIDelegate::drawHeaderInRect(
320    /* [in] */ IWebView *webView,
321    /* [in] */ RECT *rect,
322    /* [in] */ OLE_HANDLE drawingContext)
323{
324    return E_NOTIMPL;
325}
326
327HRESULT STDMETHODCALLTYPE UIDelegate::drawFooterInRect(
328    /* [in] */ IWebView *webView,
329    /* [in] */ RECT *rect,
330    /* [in] */ OLE_HANDLE drawingContext,
331    /* [in] */ UINT pageIndex,
332    /* [in] */ UINT pageCount)
333{
334    return E_NOTIMPL;
335}
336
337HRESULT STDMETHODCALLTYPE UIDelegate::webViewPrintingMarginRect(
338    /* [in] */ IWebView *webView,
339    /* [retval][out] */ RECT *rect)
340{
341    return E_NOTIMPL;
342}
343
344HRESULT STDMETHODCALLTYPE UIDelegate::canRunModal(
345    /* [in] */ IWebView *webView,
346    /* [retval][out] */ BOOL *canRunBoolean)
347{
348    return E_NOTIMPL;
349}
350
351HRESULT STDMETHODCALLTYPE UIDelegate::createModalDialog(
352    /* [in] */ IWebView *sender,
353    /* [in] */ IWebURLRequest *request,
354    /* [retval][out] */ IWebView **newWebView)
355{
356    return E_NOTIMPL;
357}
358
359HRESULT STDMETHODCALLTYPE UIDelegate::runModal(
360    /* [in] */ IWebView *webView)
361{
362    return E_NOTIMPL;
363}
364
365HRESULT STDMETHODCALLTYPE UIDelegate::isMenuBarVisible(
366    /* [in] */ IWebView *webView,
367    /* [retval][out] */ BOOL *visible)
368{
369    if (!visible)
370        return E_POINTER;
371    *visible = false;
372    return E_NOTIMPL;
373}
374
375HRESULT STDMETHODCALLTYPE UIDelegate::setMenuBarVisible(
376    /* [in] */ IWebView *webView,
377    /* [in] */ BOOL visible)
378{
379    return E_NOTIMPL;
380}
381
382HRESULT STDMETHODCALLTYPE UIDelegate::runDatabaseSizeLimitPrompt(
383    /* [in] */ IWebView *webView,
384    /* [in] */ BSTR displayName,
385    /* [in] */ IWebFrame *initiatedByFrame,
386    /* [retval][out] */ BOOL *allowed)
387{
388    if (!allowed)
389        return E_POINTER;
390    *allowed = false;
391    return E_NOTIMPL;
392}
393
394HRESULT STDMETHODCALLTYPE UIDelegate::paintCustomScrollbar(
395    /* [in] */ IWebView *webView,
396    /* [in] */ HDC hDC,
397    /* [in] */ RECT rect,
398    /* [in] */ WebScrollBarControlSize size,
399    /* [in] */ WebScrollbarControlState state,
400    /* [in] */ WebScrollbarControlPart pressedPart,
401    /* [in] */ BOOL vertical,
402    /* [in] */ float value,
403    /* [in] */ float proportion,
404    /* [in] */ WebScrollbarControlPartMask parts)
405{
406    return E_NOTIMPL;
407}
408
409HRESULT STDMETHODCALLTYPE UIDelegate::paintCustomScrollCorner(
410    /* [in] */ IWebView *webView,
411    /* [in] */ HDC hDC,
412    /* [in] */ RECT rect)
413{
414    return E_NOTIMPL;
415}
416
417HRESULT STDMETHODCALLTYPE UIDelegate::setFrame(
418        /* [in] */ IWebView* /*sender*/,
419        /* [in] */ RECT* frame)
420{
421    m_frame = *frame;
422    return S_OK;
423}
424
425HRESULT STDMETHODCALLTYPE UIDelegate::webViewFrame(
426        /* [in] */ IWebView* /*sender*/,
427        /* [retval][out] */ RECT* frame)
428{
429    *frame = m_frame;
430    return S_OK;
431}
432
433HRESULT STDMETHODCALLTYPE UIDelegate::runJavaScriptAlertPanelWithMessage(
434        /* [in] */ IWebView* /*sender*/,
435        /* [in] */ BSTR message)
436{
437    printf("ALERT: %S\n", message ? message : L"");
438
439    return S_OK;
440}
441
442HRESULT STDMETHODCALLTYPE UIDelegate::runJavaScriptConfirmPanelWithMessage(
443    /* [in] */ IWebView* sender,
444    /* [in] */ BSTR message,
445    /* [retval][out] */ BOOL* result)
446{
447    printf("CONFIRM: %S\n", message ? message : L"");
448    *result = TRUE;
449
450    return S_OK;
451}
452
453HRESULT STDMETHODCALLTYPE UIDelegate::runJavaScriptTextInputPanelWithPrompt(
454    /* [in] */ IWebView *sender,
455    /* [in] */ BSTR message,
456    /* [in] */ BSTR defaultText,
457    /* [retval][out] */ BSTR *result)
458{
459    printf("PROMPT: %S, default text: %S\n", message ? message : L"", defaultText ? defaultText : L"");
460    *result = SysAllocString(defaultText);
461
462    return S_OK;
463}
464
465HRESULT STDMETHODCALLTYPE UIDelegate::runBeforeUnloadConfirmPanelWithMessage(
466    /* [in] */ IWebView* /*sender*/,
467    /* [in] */ BSTR /*message*/,
468    /* [in] */ IWebFrame* /*initiatedByFrame*/,
469    /* [retval][out] */ BOOL* result)
470{
471    if (!result)
472        return E_POINTER;
473    *result = TRUE;
474    return E_NOTIMPL;
475}
476
477HRESULT STDMETHODCALLTYPE UIDelegate::webViewAddMessageToConsole(
478    /* [in] */ IWebView* sender,
479    /* [in] */ BSTR message,
480    /* [in] */ int lineNumber,
481    /* [in] */ BSTR url,
482    /* [in] */ BOOL isError)
483{
484    wstring newMessage;
485    if (message) {
486        newMessage = message;
487        size_t fileProtocol = newMessage.find(L"file://");
488        if (fileProtocol != wstring::npos)
489            newMessage = newMessage.substr(0, fileProtocol) + lastPathComponent(newMessage.substr(fileProtocol));
490    }
491
492    printf("CONSOLE MESSAGE: line %d: %s\n", lineNumber, toUTF8(newMessage).c_str());
493    return S_OK;
494}
495
496HRESULT STDMETHODCALLTYPE UIDelegate::doDragDrop(
497    /* [in] */ IWebView* sender,
498    /* [in] */ IDataObject* object,
499    /* [in] */ IDropSource* source,
500    /* [in] */ DWORD okEffect,
501    /* [retval][out] */ DWORD* performedEffect)
502{
503    if (!performedEffect)
504        return E_POINTER;
505
506    *performedEffect = 0;
507
508    draggingInfo = new DraggingInfo(object, source);
509    HRESULT oleDragAndDropReturnValue = DRAGDROP_S_CANCEL;
510    replaySavedEvents(&oleDragAndDropReturnValue);
511    if (draggingInfo) {
512        *performedEffect = draggingInfo->performedDropEffect();
513        delete draggingInfo;
514        draggingInfo = 0;
515    }
516    return oleDragAndDropReturnValue;
517}
518
519HRESULT STDMETHODCALLTYPE UIDelegate::webViewGetDlgCode(
520    /* [in] */ IWebView* /*sender*/,
521    /* [in] */ UINT /*keyCode*/,
522    /* [retval][out] */ LONG_PTR *code)
523{
524    if (!code)
525        return E_POINTER;
526    *code = 0;
527    return E_NOTIMPL;
528}
529
530HRESULT STDMETHODCALLTYPE UIDelegate::createWebViewWithRequest(
531        /* [in] */ IWebView *sender,
532        /* [in] */ IWebURLRequest *request,
533        /* [retval][out] */ IWebView **newWebView)
534{
535    if (!::gLayoutTestController->canOpenWindows())
536        return E_FAIL;
537    *newWebView = createWebViewAndOffscreenWindow();
538    return S_OK;
539}
540
541HRESULT STDMETHODCALLTYPE UIDelegate::webViewClose(
542        /* [in] */ IWebView *sender)
543{
544    HWND hostWindow;
545    sender->hostWindow(reinterpret_cast<OLE_HANDLE*>(&hostWindow));
546    DestroyWindow(hostWindow);
547    return S_OK;
548}
549
550HRESULT STDMETHODCALLTYPE UIDelegate::webViewFocus(
551        /* [in] */ IWebView *sender)
552{
553    HWND hostWindow;
554    sender->hostWindow(reinterpret_cast<OLE_HANDLE*>(&hostWindow));
555    SetForegroundWindow(hostWindow);
556    return S_OK;
557}
558
559HRESULT STDMETHODCALLTYPE UIDelegate::webViewUnfocus(
560        /* [in] */ IWebView *sender)
561{
562    SetForegroundWindow(GetDesktopWindow());
563    return S_OK;
564}
565
566HRESULT STDMETHODCALLTYPE UIDelegate::webViewPainted(
567        /* [in] */ IWebView *sender)
568{
569    return S_OK;
570}
571
572HRESULT STDMETHODCALLTYPE UIDelegate::exceededDatabaseQuota(
573        /* [in] */ IWebView *sender,
574        /* [in] */ IWebFrame *frame,
575        /* [in] */ IWebSecurityOrigin *origin,
576        /* [in] */ BSTR databaseIdentifier)
577{
578    BSTR protocol;
579    BSTR host;
580    unsigned short port;
581
582    origin->protocol(&protocol);
583    origin->host(&host);
584    origin->port(&port);
585
586    if (!done && gLayoutTestController->dumpDatabaseCallbacks())
587        printf("UI DELEGATE DATABASE CALLBACK: exceededDatabaseQuotaForSecurityOrigin:{%S, %S, %i} database:%S\n", protocol, host, port, databaseIdentifier);
588
589    SysFreeString(protocol);
590    SysFreeString(host);
591
592    static const unsigned long long defaultQuota = 5 * 1024 * 1024;
593    origin->setQuota(defaultQuota);
594
595    return S_OK;
596}
597
598HRESULT STDMETHODCALLTYPE UIDelegate::embeddedViewWithArguments(
599    /* [in] */ IWebView *sender,
600    /* [in] */ IWebFrame *frame,
601    /* [in] */ IPropertyBag *arguments,
602    /* [retval][out] */ IWebEmbeddedView **view)
603{
604    if (!view)
605        return E_POINTER;
606    *view = 0;
607    return E_NOTIMPL;
608}
609
610HRESULT STDMETHODCALLTYPE UIDelegate::webViewClosing(
611    /* [in] */ IWebView *sender)
612{
613    return E_NOTIMPL;
614}
615
616HRESULT STDMETHODCALLTYPE UIDelegate::webViewSetCursor(
617    /* [in] */ IWebView *sender,
618    /* [in] */ OLE_HANDLE cursor)
619{
620    return E_NOTIMPL;
621}
622
623HRESULT STDMETHODCALLTYPE UIDelegate::webViewDidInvalidate(
624    /* [in] */ IWebView *sender)
625{
626    return E_NOTIMPL;
627}
628
629HRESULT STDMETHODCALLTYPE UIDelegate::setStatusText(IWebView*, BSTR text)
630{
631    if (gLayoutTestController->dumpStatusCallbacks())
632        printf("UI DELEGATE STATUS CALLBACK: setStatusText:%s\n", text ? toUTF8(text).c_str() : "");
633    return S_OK;
634}
635
636HRESULT STDMETHODCALLTYPE UIDelegate::desktopNotificationsDelegate(IWebDesktopNotificationsDelegate** result)
637{
638    m_desktopNotifications.copyRefTo(result);
639    return S_OK;
640}
641
642HRESULT STDMETHODCALLTYPE UIDelegate::createWebViewWithRequest(IWebView* sender, IWebURLRequest* request, IPropertyBag* windowFeatures, IWebView** newWebView)
643{
644    return E_NOTIMPL;
645}
646
647HRESULT STDMETHODCALLTYPE UIDelegate::drawBackground(IWebView* sender, OLE_HANDLE hdc, const RECT* dirtyRect)
648{
649    return E_NOTIMPL;
650}
651
652HRESULT STDMETHODCALLTYPE UIDelegate::decidePolicyForGeolocationRequest(IWebView* sender, IWebFrame* frame, IWebSecurityOrigin* origin, IWebGeolocationPolicyListener* listener)
653{
654    return E_NOTIMPL;
655}
656
657HRESULT STDMETHODCALLTYPE UIDelegate::didPressMissingPluginButton(IDOMElement* element)
658{
659    printf("MISSING PLUGIN BUTTON PRESSED\n");
660    return S_OK;
661}
662
663