1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #define STRSAFE_NO_DEPRECATE
7 #include "content/test/plugin/plugin_windowless_test.h"
9 #include "base/strings/string_number_conversions.h"
10 #include "base/strings/string_util.h"
11 #include "content/test/plugin/plugin_client.h"
13 #if defined(TOOLKIT_GTK)
17 // NPEvent does not exist on the Mac.
18 #if defined(OS_MACOSX)
19 typedef NPCocoaEvent WindowlessPluginTestEvent
;
21 typedef NPEvent WindowlessPluginTestEvent
;
24 namespace NPAPIClient
{
28 // Remember the first plugin instance for tests involving multiple instances.
29 WindowlessPluginTest
* g_other_instance
= NULL
;
31 void OnFinishTest(void* data
) {
32 static_cast<WindowlessPluginTest
*>(data
)->SignalTestCompleted();
35 bool IsPaintEvent(WindowlessPluginTestEvent
* np_event
) {
37 return np_event
->event
== WM_PAINT
;
38 #elif defined(OS_MACOSX)
39 return np_event
->type
== NPCocoaEventDrawRect
;
40 #elif defined(TOOLKIT_GTK)
41 return np_event
->type
== GraphicsExpose
;
48 bool IsMouseUpEvent(WindowlessPluginTestEvent
* np_event
) {
50 return np_event
->event
== WM_LBUTTONUP
;
51 #elif defined(OS_MACOSX)
52 return np_event
->type
== NPCocoaEventMouseUp
;
59 #if defined(OS_MACOSX)
60 bool IsWindowActivationEvent(WindowlessPluginTestEvent
* np_event
) {
61 return np_event
->type
== NPCocoaEventWindowFocusChanged
&&
62 np_event
->data
.focus
.hasFocus
;
68 WindowlessPluginTest::WindowlessPluginTest(NPP id
,
69 NPNetscapeFuncs
*host_functions
)
70 : PluginTest(id
, host_functions
),
72 if (!g_other_instance
)
73 g_other_instance
= this;
76 bool WindowlessPluginTest::IsWindowless() const {
80 NPError
WindowlessPluginTest::New(uint16 mode
, int16 argc
,
81 const char* argn
[], const char* argv
[],
83 NPError error
= PluginTest::New(mode
, argc
, argn
, argv
, saved
);
85 if (test_name() == "invoke_js_function_on_create") {
87 NPAPIClient::PluginClient::HostFunctions(), g_other_instance
->id(),
88 "PluginCreated();", NULL
);
94 int16
WindowlessPluginTest::HandleEvent(void* event
) {
95 NPNetscapeFuncs
* browser
= NPAPIClient::PluginClient::HostFunctions();
97 NPBool supports_windowless
= 0;
98 NPError result
= browser
->getvalue(id(), NPNVSupportsWindowless
,
99 &supports_windowless
);
100 if ((result
!= NPERR_NO_ERROR
) || (!supports_windowless
)) {
101 SetError("Failed to read NPNVSupportsWindowless value");
102 SignalTestCompleted();
103 return PluginTest::HandleEvent(event
);
106 WindowlessPluginTestEvent
* np_event
=
107 reinterpret_cast<WindowlessPluginTestEvent
*>(event
);
108 if (IsPaintEvent(np_event
)) {
111 HDC paint_dc
= reinterpret_cast<HDC
>(np_event
->wParam
);
112 if (paint_dc
== NULL
) {
113 SetError("Invalid Window DC passed to HandleEvent for WM_PAINT");
114 SignalTestCompleted();
115 return NPERR_GENERIC_ERROR
;
118 HRGN clipping_region
= CreateRectRgn(0, 0, 0, 0);
119 if (!GetClipRgn(paint_dc
, clipping_region
)) {
120 SetError("No clipping region set in window DC");
121 DeleteObject(clipping_region
);
122 SignalTestCompleted();
123 return NPERR_GENERIC_ERROR
;
126 DeleteObject(clipping_region
);
129 if (test_name() == "execute_script_delete_in_paint") {
130 ExecuteScriptDeleteInPaint(browser
);
131 } else if (test_name() == "multiple_instances_sync_calls") {
132 MultipleInstanceSyncCalls(browser
);
133 } else if (test_name() == "resize_during_paint") {
134 if (paint_counter_
== 1) {
135 // So that we get another paint later.
136 browser
->invalidaterect(id(), NULL
);
137 } else if (paint_counter_
== 2) {
138 // Do this in the second paint since that's asynchronous. The first
139 // paint will always be synchronous (since the renderer process doesn't
140 // have a cache of the plugin yet). If we try calling NPN_Evaluate while
141 // WebKit is painting, it will assert since style recalc is happening
143 ExecuteScriptResizeInPaint(browser
);
145 // So that we can exit the test after the message loop is unrolled.
146 browser
->pluginthreadasynccall(id(), OnFinishTest
, this);
149 #if defined(OS_MACOSX)
150 } else if (IsWindowActivationEvent(np_event
) &&
151 test_name() == "convert_point") {
152 ConvertPoint(browser
);
154 } else if (IsMouseUpEvent(np_event
) &&
155 test_name() == "execute_script_delete_in_mouse_up") {
156 ExecuteScript(browser
, id(), "DeletePluginWithinScript();", NULL
);
157 SignalTestCompleted();
158 } else if (IsMouseUpEvent(np_event
) &&
159 test_name() == "delete_frame_test") {
162 "parent.document.getElementById('frame').outerHTML = ''", NULL
);
164 // If this test failed, then we'd have crashed by now.
165 return PluginTest::HandleEvent(event
);
168 NPError
WindowlessPluginTest::ExecuteScript(NPNetscapeFuncs
* browser
, NPP id
,
169 const std::string
& script
, NPVariant
* result
) {
170 std::string script_url
= "javascript:";
171 script_url
+= script
;
173 size_t script_length
= script_url
.length();
174 if (script_length
!= static_cast<uint32_t>(script_length
)) {
175 return NPERR_GENERIC_ERROR
;
178 NPString script_string
= { script_url
.c_str(),
179 static_cast<uint32_t>(script_length
) };
180 NPObject
*window_obj
= NULL
;
181 browser
->getvalue(id
, NPNVWindowNPObject
, &window_obj
);
183 NPVariant unused_result
;
185 result
= &unused_result
;
187 return browser
->evaluate(id
, window_obj
, &script_string
, result
);
190 void WindowlessPluginTest::ExecuteScriptDeleteInPaint(
191 NPNetscapeFuncs
* browser
) {
192 const NPUTF8
* urlString
= "javascript:DeletePluginWithinScript()";
193 const NPUTF8
* targetString
= NULL
;
194 browser
->geturl(id(), urlString
, targetString
);
195 SignalTestCompleted();
198 void WindowlessPluginTest::ExecuteScriptResizeInPaint(
199 NPNetscapeFuncs
* browser
) {
200 ExecuteScript(browser
, id(), "ResizePluginWithinScript();", NULL
);
203 void WindowlessPluginTest::MultipleInstanceSyncCalls(NPNetscapeFuncs
* browser
) {
204 if (this == g_other_instance
)
207 DCHECK(g_other_instance
);
208 ExecuteScript(browser
, g_other_instance
->id(), "TestCallback();", NULL
);
209 SignalTestCompleted();
212 #if defined(OS_MACOSX)
213 std::string
StringForPoint(int x
, int y
) {
214 std::string
point_string("(");
215 point_string
.append(base::IntToString(x
));
216 point_string
.append(", ");
217 point_string
.append(base::IntToString(y
));
218 point_string
.append(")");
223 void WindowlessPluginTest::ConvertPoint(NPNetscapeFuncs
* browser
) {
224 #if defined(OS_MACOSX)
225 // First, just sanity-test that round trips work.
226 NPCoordinateSpace spaces
[] = { NPCoordinateSpacePlugin
,
227 NPCoordinateSpaceWindow
,
228 NPCoordinateSpaceFlippedWindow
,
229 NPCoordinateSpaceScreen
,
230 NPCoordinateSpaceFlippedScreen
};
231 for (unsigned int i
= 0; i
< arraysize(spaces
); ++i
) {
232 for (unsigned int j
= 0; j
< arraysize(spaces
); ++j
) {
233 double x
, y
, round_trip_x
, round_trip_y
;
234 if (!(browser
->convertpoint(id(), 0, 0, spaces
[i
], &x
, &y
, spaces
[j
])) ||
235 !(browser
->convertpoint(id(), x
, y
, spaces
[j
], &round_trip_x
,
236 &round_trip_y
, spaces
[i
]))) {
237 SetError("Conversion failed");
238 SignalTestCompleted();
241 if (i
!= j
&& x
== 0 && y
== 0) {
242 SetError("Converting a coordinate should change it");
243 SignalTestCompleted();
246 if (round_trip_x
!= 0 || round_trip_y
!= 0) {
247 SetError("Round-trip conversion should return the original point");
248 SignalTestCompleted();
254 // Now, more extensive testing on a single point.
255 double screen_x
, screen_y
;
256 browser
->convertpoint(id(), 0, 0, NPCoordinateSpacePlugin
,
257 &screen_x
, &screen_y
, NPCoordinateSpaceScreen
);
258 double flipped_screen_x
, flipped_screen_y
;
259 browser
->convertpoint(id(), 0, 0, NPCoordinateSpacePlugin
,
260 &flipped_screen_x
, &flipped_screen_y
,
261 NPCoordinateSpaceFlippedScreen
);
262 double window_x
, window_y
;
263 browser
->convertpoint(id(), 0, 0, NPCoordinateSpacePlugin
,
264 &window_x
, &window_y
, NPCoordinateSpaceWindow
);
265 double flipped_window_x
, flipped_window_y
;
266 browser
->convertpoint(id(), 0, 0, NPCoordinateSpacePlugin
,
267 &flipped_window_x
, &flipped_window_y
,
268 NPCoordinateSpaceFlippedWindow
);
270 CGRect main_display_bounds
= CGDisplayBounds(CGMainDisplayID());
272 // Check that all the coordinates are right. The constants below are based on
273 // the window frame set in the UI test and the content offset in the test
274 // html. Y-coordinates are not checked exactly so that the test is robust
275 // against toolbar changes, info and bookmark bar visibility, etc.
276 const int kWindowHeight
= 400;
277 const int kWindowXOrigin
= 50;
278 const int kWindowYOrigin
= 50;
279 const int kPluginXContentOffset
= 50;
280 const int kPluginYContentOffset
= 50;
281 const int kChromeYTolerance
= 200;
283 std::string error_string
;
284 if (screen_x
!= flipped_screen_x
)
285 error_string
= "Flipping screen coordinates shouldn't change x";
286 else if (flipped_screen_y
!= main_display_bounds
.size
.height
- screen_y
)
287 error_string
= "Flipped screen coordinates should be flipped vertically";
288 else if (screen_x
!= kWindowXOrigin
+ kPluginXContentOffset
)
289 error_string
= "Screen x location is wrong";
290 else if (flipped_screen_y
< kWindowYOrigin
+ kPluginYContentOffset
||
291 flipped_screen_y
> kWindowYOrigin
+ kPluginYContentOffset
+
293 error_string
= "Screen y location is wrong";
294 else if (window_x
!= flipped_window_x
)
295 error_string
= "Flipping window coordinates shouldn't change x";
296 else if (flipped_window_y
!= kWindowHeight
- window_y
)
297 error_string
= "Flipped window coordinates should be flipped vertically";
298 else if (window_x
!= kPluginXContentOffset
)
299 error_string
= "Window x location is wrong";
300 else if (flipped_window_y
< kPluginYContentOffset
||
301 flipped_window_y
> kPluginYContentOffset
+ kChromeYTolerance
)
302 error_string
= "Window y location is wrong";
304 if (!error_string
.empty()) {
305 error_string
.append(" - ");
306 error_string
.append(StringForPoint(screen_x
, screen_y
));
307 error_string
.append(" - ");
308 error_string
.append(StringForPoint(flipped_screen_x
, flipped_screen_y
));
309 error_string
.append(" - ");
310 error_string
.append(StringForPoint(window_x
, window_y
));
311 error_string
.append(" - ");
312 error_string
.append(StringForPoint(flipped_window_x
, flipped_window_y
));
313 SetError(error_string
);
316 SetError("Unimplemented");
318 SignalTestCompleted();
321 } // namespace NPAPIClient