1/*
2 * Copyright (C) 2010 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 * 1.  Redistributions of source code must retain the above copyright
8 *     notice, this list of conditions and the following disclaimer.
9 * 2.  Redistributions in binary form must reproduce the above copyright
10 *     notice, this list of conditions and the following disclaimer in the
11 *     documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16 * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
17 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 */
24
25#include "config.h"
26
27#include "HostWindow.h"
28#include "Test.h"
29#include <WebCore/COMPtr.h>
30#include <WebKit/WebKit.h>
31#include <WebKit/WebKitCOMAPI.h>
32#include <wtf/PassOwnPtr.h>
33
34namespace WebKitAPITest {
35
36template <typename T>
37static HRESULT WebKitCreateInstance(REFCLSID clsid, T** object)
38{
39    return WebKitCreateInstance(clsid, 0, __uuidof(T), reinterpret_cast<void**>(object));
40}
41
42static int webViewCount()
43{
44    COMPtr<IWebKitStatistics> statistics;
45    if (FAILED(WebKitCreateInstance(__uuidof(WebKitStatistics), &statistics)))
46        return -1;
47    int count;
48    if (FAILED(statistics->webViewCount(&count)))
49        return -1;
50    return count;
51}
52
53static void createAndInitializeWebView(COMPtr<IWebView>& outWebView, HostWindow& window, HWND& viewWindow)
54{
55    COMPtr<IWebView> webView;
56    TEST_ASSERT(SUCCEEDED(WebKitCreateInstance(__uuidof(WebView), &webView)));
57
58    TEST_ASSERT(window.initialize());
59    TEST_ASSERT(SUCCEEDED(webView->setHostWindow(reinterpret_cast<OLE_HANDLE>(window.window()))));
60    TEST_ASSERT(SUCCEEDED(webView->initWithFrame(window.clientRect(), 0, 0)));
61
62    COMPtr<IWebViewPrivate> viewPrivate(Query, webView);
63    TEST_ASSERT(viewPrivate);
64    TEST_ASSERT(SUCCEEDED(viewPrivate->viewWindow(reinterpret_cast<OLE_HANDLE*>(&viewWindow))));
65    TEST_ASSERT(IsWindow(viewWindow));
66
67    outWebView.adoptRef(webView.releaseRef());
68}
69
70static void runMessagePump(DWORD timeoutMilliseconds)
71{
72    DWORD startTickCount = GetTickCount();
73    MSG msg;
74    BOOL result;
75    while ((result = PeekMessageW(&msg, 0, 0, 0, PM_REMOVE)) && GetTickCount() - startTickCount <= timeoutMilliseconds) {
76        if (result == -1)
77            break;
78        TranslateMessage(&msg);
79        DispatchMessage(&msg);
80    }
81}
82
83static void finishWebViewDestructionTest(COMPtr<IWebView>& webView, HWND viewWindow)
84{
85    // Allow window messages to be processed, because in some cases that would trigger a crash (e.g., <http://webkit.org/b/32827>).
86    runMessagePump(50);
87
88    // We haven't crashed. Release the WebView and ensure that its view window has been destroyed and the WebView doesn't leak.
89    int currentWebViewCount = webViewCount();
90    TEST_ASSERT(currentWebViewCount > 0);
91
92    webView = 0;
93
94    TEST_ASSERT(webViewCount() == currentWebViewCount - 1);
95    TEST_ASSERT(!IsWindow(viewWindow));
96}
97
98// Tests that releasing a WebView without calling IWebView::initWithFrame works.
99TEST(WebViewDestruction, NoInitWithFrame)
100{
101    COMPtr<IWebView> webView;
102    TEST_ASSERT(SUCCEEDED(WebKitCreateInstance(__uuidof(WebView), &webView)));
103
104    finishWebViewDestructionTest(webView, 0);
105}
106
107TEST(WebViewDestruction, CloseWithoutInitWithFrame)
108{
109    COMPtr<IWebView> webView;
110    TEST_ASSERT(SUCCEEDED(WebKitCreateInstance(__uuidof(WebView), &webView)));
111
112    TEST_ASSERT(SUCCEEDED(webView->close()));
113
114    finishWebViewDestructionTest(webView, 0);
115}
116
117// Tests that releasing a WebView without calling IWebView::close or DestroyWindow doesn't leak. <http://webkit.org/b/33162>
118TEST(WebViewDestruction, NoCloseOrDestroyViewWindow)
119{
120    COMPtr<IWebView> webView;
121    HostWindow window;
122    HWND viewWindow;
123    createAndInitializeWebView(webView, window, viewWindow);
124
125    finishWebViewDestructionTest(webView, viewWindow);
126}
127
128// Tests that calling IWebView::close without calling DestroyWindow, then releasing a WebView doesn't crash. <http://webkit.org/b/32827>
129TEST(WebViewDestruction, CloseWithoutDestroyViewWindow)
130{
131    COMPtr<IWebView> webView;
132    HostWindow window;
133    HWND viewWindow;
134    createAndInitializeWebView(webView, window, viewWindow);
135
136    TEST_ASSERT(SUCCEEDED(webView->close()));
137
138    finishWebViewDestructionTest(webView, viewWindow);
139}
140
141TEST(WebViewDestruction, DestroyViewWindowWithoutClose)
142{
143    COMPtr<IWebView> webView;
144    HostWindow window;
145    HWND viewWindow;
146    createAndInitializeWebView(webView, window, viewWindow);
147
148    DestroyWindow(viewWindow);
149
150    finishWebViewDestructionTest(webView, viewWindow);
151}
152
153TEST(WebViewDestruction, CloseThenDestroyViewWindow)
154{
155    COMPtr<IWebView> webView;
156    HostWindow window;
157    HWND viewWindow;
158    createAndInitializeWebView(webView, window, viewWindow);
159
160    TEST_ASSERT(SUCCEEDED(webView->close()));
161    DestroyWindow(viewWindow);
162
163    finishWebViewDestructionTest(webView, viewWindow);
164}
165
166TEST(WebViewDestruction, DestroyViewWindowThenClose)
167{
168    COMPtr<IWebView> webView;
169    HostWindow window;
170    HWND viewWindow;
171    createAndInitializeWebView(webView, window, viewWindow);
172
173    DestroyWindow(viewWindow);
174    TEST_ASSERT(SUCCEEDED(webView->close()));
175
176    finishWebViewDestructionTest(webView, viewWindow);
177}
178
179TEST(WebViewDestruction, DestroyHostWindow)
180{
181    COMPtr<IWebView> webView;
182    HostWindow window;
183    HWND viewWindow;
184    createAndInitializeWebView(webView, window, viewWindow);
185
186    DestroyWindow(window.window());
187
188    finishWebViewDestructionTest(webView, viewWindow);
189}
190
191TEST(WebViewDestruction, DestroyHostWindowThenClose)
192{
193    COMPtr<IWebView> webView;
194    HostWindow window;
195    HWND viewWindow;
196    createAndInitializeWebView(webView, window, viewWindow);
197
198    DestroyWindow(window.window());
199    TEST_ASSERT(SUCCEEDED(webView->close()));
200
201    finishWebViewDestructionTest(webView, viewWindow);
202}
203
204TEST(WebViewDestruction, CloseThenDestroyHostWindow)
205{
206    COMPtr<IWebView> webView;
207    HostWindow window;
208    HWND viewWindow;
209    createAndInitializeWebView(webView, window, viewWindow);
210
211    TEST_ASSERT(SUCCEEDED(webView->close()));
212    DestroyWindow(window.window());
213
214    finishWebViewDestructionTest(webView, viewWindow);
215}
216
217// Tests that calling IWebView::mainFrame after calling IWebView::close doesn't crash. <http://webkit.org/b/32868>
218TEST(WebViewDestruction, MainFrameAfterClose)
219{
220    COMPtr<IWebView> webView;
221    HostWindow window;
222    HWND viewWindow;
223    createAndInitializeWebView(webView, window, viewWindow);
224
225    TEST_ASSERT(SUCCEEDED(webView->close()));
226    COMPtr<IWebFrame> mainFrame;
227    TEST_ASSERT(SUCCEEDED(webView->mainFrame(&mainFrame)));
228
229    finishWebViewDestructionTest(webView, viewWindow);
230}
231
232} // namespace WebKitAPITest
233