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''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "WindowedPluginTest.h"
27
28#include "PluginObject.h"
29
30using namespace std;
31
32// NPN_InvalidateRect should invalidate the plugin's HWND.
33
34static const wchar_t instancePointerProperty[] = L"org.webkit.TestNetscapePlugin.NPNInvalidateRectInvalidatesWindow.InstancePointer";
35
36class TemporaryWindowMover {
37public:
38    TemporaryWindowMover(HWND);
39    ~TemporaryWindowMover();
40
41    bool moveSucceeded() const { return m_moveSucceeded; }
42
43private:
44    static const UINT standardSetWindowPosFlags = SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOSIZE | SWP_NOZORDER;
45    bool m_moveSucceeded;
46    HWND m_window;
47    RECT m_savedWindowRect;
48};
49
50TemporaryWindowMover::TemporaryWindowMover(HWND window)
51    : m_window(window)
52{
53    m_moveSucceeded = false;
54
55    if (!::GetWindowRect(m_window, &m_savedWindowRect))
56        return;
57
58    if (!::SetWindowPos(m_window, 0, 0, 0, 0, 0, SWP_SHOWWINDOW | standardSetWindowPosFlags))
59        return;
60
61    m_moveSucceeded = true;
62};
63
64TemporaryWindowMover::~TemporaryWindowMover()
65{
66    if (!m_moveSucceeded)
67        return;
68
69    ::SetWindowPos(m_window, 0, m_savedWindowRect.left, m_savedWindowRect.top, 0, 0, SWP_HIDEWINDOW | standardSetWindowPosFlags);
70}
71
72class NPNInvalidateRectInvalidatesWindow : public WindowedPluginTest {
73public:
74    NPNInvalidateRectInvalidatesWindow(NPP, const string& identifier);
75    ~NPNInvalidateRectInvalidatesWindow();
76
77private:
78    virtual LRESULT wndProc(UINT message, WPARAM, LPARAM, bool& handled);
79
80    void onPaint();
81    void testInvalidateRect();
82
83    virtual NPError NPP_SetWindow(NPP, NPWindow*);
84
85    TemporaryWindowMover* m_windowMover;
86};
87
88NPNInvalidateRectInvalidatesWindow::NPNInvalidateRectInvalidatesWindow(NPP npp, const string& identifier)
89    : WindowedPluginTest(npp, identifier)
90    , m_windowMover(0)
91{
92}
93
94NPNInvalidateRectInvalidatesWindow::~NPNInvalidateRectInvalidatesWindow()
95{
96    delete m_windowMover;
97}
98
99NPError NPNInvalidateRectInvalidatesWindow::NPP_SetWindow(NPP instance, NPWindow* npWindow)
100{
101    NPError error = WindowedPluginTest::NPP_SetWindow(instance, npWindow);
102    if (error != NPERR_NO_ERROR)
103        return error;
104
105    if (!window())
106        return NPERR_NO_ERROR;
107
108    // The test harness's window (the one that contains the WebView) is off-screen and hidden.
109    // We need to move it on-screen and make it visible in order for the plugin's window to
110    // accumulate an update region when the DWM is disabled.
111
112    HWND testHarnessWindow = ::GetAncestor(window(), GA_ROOT);
113    if (!testHarnessWindow) {
114        pluginLog(instance, "Failed to get test harness window");
115        return NPERR_GENERIC_ERROR;
116    }
117
118    m_windowMover = new TemporaryWindowMover(testHarnessWindow);
119    if (!m_windowMover->moveSucceeded()) {
120        pluginLog(instance, "Failed to move test harness window on-screen");
121        return NPERR_GENERIC_ERROR;
122    }
123
124    // Wait until we receive a WM_PAINT message to ensure that the window is on-screen before we do
125    // the NPN_InvalidateRect test.
126    waitUntilDone();
127    return NPERR_NO_ERROR;
128}
129
130LRESULT NPNInvalidateRectInvalidatesWindow::wndProc(UINT message, WPARAM wParam, LPARAM lParam, bool& handled)
131{
132    if (message == WM_PAINT)
133        onPaint();
134
135    handled = false;
136    return 0;
137}
138
139void NPNInvalidateRectInvalidatesWindow::onPaint()
140{
141    testInvalidateRect();
142    notifyDone();
143    delete m_windowMover;
144    m_windowMover = 0;
145}
146
147void NPNInvalidateRectInvalidatesWindow::testInvalidateRect()
148{
149    RECT clientRect;
150    if (!::GetClientRect(window(), &clientRect)) {
151        pluginLog(m_npp, "::GetClientRect failed");
152        return;
153    }
154
155    if (::IsRectEmpty(&clientRect)) {
156        pluginLog(m_npp, "Plugin's HWND has not been sized when NPP_SetWindow is called");
157        return;
158    }
159
160    // Clear the invalid region.
161    if (!::ValidateRect(window(), 0)) {
162        pluginLog(m_npp, "::ValidateRect failed");
163        return;
164    }
165
166    // Invalidate our lower-right quadrant.
167    NPRect rectToInvalidate;
168    rectToInvalidate.left = (clientRect.right - clientRect.left) / 2;
169    rectToInvalidate.top = (clientRect.bottom - clientRect.top) / 2;
170    rectToInvalidate.right = clientRect.right;
171    rectToInvalidate.bottom = clientRect.bottom;
172    NPN_InvalidateRect(&rectToInvalidate);
173
174    RECT invalidRect;
175    if (!::GetUpdateRect(window(), &invalidRect, FALSE)) {
176        pluginLog(m_npp, "::GetUpdateRect failed");
177        return;
178    }
179
180    if (invalidRect.left != rectToInvalidate.left || invalidRect.top != rectToInvalidate.top || invalidRect.right != rectToInvalidate.right || invalidRect.bottom != rectToInvalidate.bottom) {
181        pluginLog(m_npp, "Expected invalid rect {left=%u, top=%u, right=%u, bottom=%u}, but got {left=%d, top=%d, right=%d, bottom=%d}", rectToInvalidate.left, rectToInvalidate.top, rectToInvalidate.right, rectToInvalidate.bottom, invalidRect.left, invalidRect.top, invalidRect.right, invalidRect.bottom);
182        return;
183    }
184
185    pluginLog(m_npp, "Plugin's HWND has been invalidated as expected");
186}
187
188static PluginTest::Register<NPNInvalidateRectInvalidatesWindow> registrar("npn-invalidate-rect-invalidates-window");
189