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 // NPEvent does not exist on the Mac.
14 #if defined(OS_MACOSX)
15 typedef NPCocoaEvent WindowlessPluginTestEvent
;
17 typedef NPEvent WindowlessPluginTestEvent
;
20 namespace NPAPIClient
{
24 // Remember the first plugin instance for tests involving multiple instances.
25 WindowlessPluginTest
* g_other_instance
= NULL
;
27 void OnFinishTest(void* data
) {
28 static_cast<WindowlessPluginTest
*>(data
)->SignalTestCompleted();
31 bool IsPaintEvent(WindowlessPluginTestEvent
* np_event
) {
33 return np_event
->event
== WM_PAINT
;
34 #elif defined(OS_MACOSX)
35 return np_event
->type
== NPCocoaEventDrawRect
;
42 bool IsMouseUpEvent(WindowlessPluginTestEvent
* np_event
) {
44 return np_event
->event
== WM_LBUTTONUP
;
45 #elif defined(OS_MACOSX)
46 return np_event
->type
== NPCocoaEventMouseUp
;
53 #if defined(OS_MACOSX)
54 bool IsWindowActivationEvent(WindowlessPluginTestEvent
* np_event
) {
55 return np_event
->type
== NPCocoaEventWindowFocusChanged
&&
56 np_event
->data
.focus
.hasFocus
;
62 WindowlessPluginTest::WindowlessPluginTest(NPP id
,
63 NPNetscapeFuncs
*host_functions
)
64 : PluginTest(id
, host_functions
),
66 if (!g_other_instance
)
67 g_other_instance
= this;
70 bool WindowlessPluginTest::IsWindowless() const {
74 NPError
WindowlessPluginTest::New(uint16 mode
, int16 argc
,
75 const char* argn
[], const char* argv
[],
77 NPError error
= PluginTest::New(mode
, argc
, argn
, argv
, saved
);
79 if (test_name() == "invoke_js_function_on_create") {
81 NPAPIClient::PluginClient::HostFunctions(), g_other_instance
->id(),
82 "PluginCreated();", NULL
);
88 int16
WindowlessPluginTest::HandleEvent(void* event
) {
89 NPNetscapeFuncs
* browser
= NPAPIClient::PluginClient::HostFunctions();
91 NPBool supports_windowless
= 0;
92 NPError result
= browser
->getvalue(id(), NPNVSupportsWindowless
,
93 &supports_windowless
);
94 if ((result
!= NPERR_NO_ERROR
) || (!supports_windowless
)) {
95 SetError("Failed to read NPNVSupportsWindowless value");
96 SignalTestCompleted();
97 return PluginTest::HandleEvent(event
);
100 WindowlessPluginTestEvent
* np_event
=
101 reinterpret_cast<WindowlessPluginTestEvent
*>(event
);
102 if (IsPaintEvent(np_event
)) {
105 HDC paint_dc
= reinterpret_cast<HDC
>(np_event
->wParam
);
106 if (paint_dc
== NULL
) {
107 SetError("Invalid Window DC passed to HandleEvent for WM_PAINT");
108 SignalTestCompleted();
109 return NPERR_GENERIC_ERROR
;
112 HRGN clipping_region
= CreateRectRgn(0, 0, 0, 0);
113 if (!GetClipRgn(paint_dc
, clipping_region
)) {
114 SetError("No clipping region set in window DC");
115 DeleteObject(clipping_region
);
116 SignalTestCompleted();
117 return NPERR_GENERIC_ERROR
;
120 DeleteObject(clipping_region
);
123 if (test_name() == "execute_script_delete_in_paint") {
124 ExecuteScriptDeleteInPaint(browser
);
125 } else if (test_name() == "multiple_instances_sync_calls") {
126 MultipleInstanceSyncCalls(browser
);
127 } else if (test_name() == "resize_during_paint") {
128 if (paint_counter_
== 1) {
129 // So that we get another paint later.
130 browser
->invalidaterect(id(), NULL
);
131 } else if (paint_counter_
== 2) {
132 // Do this in the second paint since that's asynchronous. The first
133 // paint will always be synchronous (since the renderer process doesn't
134 // have a cache of the plugin yet). If we try calling NPN_Evaluate while
135 // WebKit is painting, it will assert since style recalc is happening
137 ExecuteScriptResizeInPaint(browser
);
139 // So that we can exit the test after the message loop is unrolled.
140 browser
->pluginthreadasynccall(id(), OnFinishTest
, this);
143 #if defined(OS_MACOSX)
144 } else if (IsWindowActivationEvent(np_event
) &&
145 test_name() == "convert_point") {
146 ConvertPoint(browser
);
148 } else if (IsMouseUpEvent(np_event
) &&
149 test_name() == "execute_script_delete_in_mouse_up") {
150 ExecuteScript(browser
, id(), "DeletePluginWithinScript();", NULL
);
151 SignalTestCompleted();
152 } else if (IsMouseUpEvent(np_event
) &&
153 test_name() == "delete_frame_test") {
156 "parent.document.getElementById('frame').outerHTML = ''", NULL
);
158 // If this test failed, then we'd have crashed by now.
159 return PluginTest::HandleEvent(event
);
162 NPError
WindowlessPluginTest::ExecuteScript(NPNetscapeFuncs
* browser
, NPP id
,
163 const std::string
& script
, NPVariant
* result
) {
164 std::string script_url
= "javascript:";
165 script_url
+= script
;
167 size_t script_length
= script_url
.length();
168 if (script_length
!= static_cast<uint32_t>(script_length
)) {
169 return NPERR_GENERIC_ERROR
;
172 NPString script_string
= { script_url
.c_str(),
173 static_cast<uint32_t>(script_length
) };
174 NPObject
*window_obj
= NULL
;
175 browser
->getvalue(id
, NPNVWindowNPObject
, &window_obj
);
177 NPVariant unused_result
;
179 result
= &unused_result
;
181 return browser
->evaluate(id
, window_obj
, &script_string
, result
);
184 void WindowlessPluginTest::ExecuteScriptDeleteInPaint(
185 NPNetscapeFuncs
* browser
) {
186 const NPUTF8
* urlString
= "javascript:DeletePluginWithinScript()";
187 const NPUTF8
* targetString
= NULL
;
188 browser
->geturl(id(), urlString
, targetString
);
189 SignalTestCompleted();
192 void WindowlessPluginTest::ExecuteScriptResizeInPaint(
193 NPNetscapeFuncs
* browser
) {
194 ExecuteScript(browser
, id(), "ResizePluginWithinScript();", NULL
);
197 void WindowlessPluginTest::MultipleInstanceSyncCalls(NPNetscapeFuncs
* browser
) {
198 if (this == g_other_instance
)
201 DCHECK(g_other_instance
);
202 ExecuteScript(browser
, g_other_instance
->id(), "TestCallback();", NULL
);
203 SignalTestCompleted();
206 #if defined(OS_MACOSX)
207 std::string
StringForPoint(int x
, int y
) {
208 std::string
point_string("(");
209 point_string
.append(base::IntToString(x
));
210 point_string
.append(", ");
211 point_string
.append(base::IntToString(y
));
212 point_string
.append(")");
217 void WindowlessPluginTest::ConvertPoint(NPNetscapeFuncs
* browser
) {
218 #if defined(OS_MACOSX)
219 // First, just sanity-test that round trips work.
220 NPCoordinateSpace spaces
[] = { NPCoordinateSpacePlugin
,
221 NPCoordinateSpaceWindow
,
222 NPCoordinateSpaceFlippedWindow
,
223 NPCoordinateSpaceScreen
,
224 NPCoordinateSpaceFlippedScreen
};
225 for (unsigned int i
= 0; i
< arraysize(spaces
); ++i
) {
226 for (unsigned int j
= 0; j
< arraysize(spaces
); ++j
) {
227 double x
, y
, round_trip_x
, round_trip_y
;
228 if (!(browser
->convertpoint(id(), 0, 0, spaces
[i
], &x
, &y
, spaces
[j
])) ||
229 !(browser
->convertpoint(id(), x
, y
, spaces
[j
], &round_trip_x
,
230 &round_trip_y
, spaces
[i
]))) {
231 SetError("Conversion failed");
232 SignalTestCompleted();
235 if (i
!= j
&& x
== 0 && y
== 0) {
236 SetError("Converting a coordinate should change it");
237 SignalTestCompleted();
240 if (round_trip_x
!= 0 || round_trip_y
!= 0) {
241 SetError("Round-trip conversion should return the original point");
242 SignalTestCompleted();
248 // Now, more extensive testing on a single point.
249 double screen_x
, screen_y
;
250 browser
->convertpoint(id(), 0, 0, NPCoordinateSpacePlugin
,
251 &screen_x
, &screen_y
, NPCoordinateSpaceScreen
);
252 double flipped_screen_x
, flipped_screen_y
;
253 browser
->convertpoint(id(), 0, 0, NPCoordinateSpacePlugin
,
254 &flipped_screen_x
, &flipped_screen_y
,
255 NPCoordinateSpaceFlippedScreen
);
256 double window_x
, window_y
;
257 browser
->convertpoint(id(), 0, 0, NPCoordinateSpacePlugin
,
258 &window_x
, &window_y
, NPCoordinateSpaceWindow
);
259 double flipped_window_x
, flipped_window_y
;
260 browser
->convertpoint(id(), 0, 0, NPCoordinateSpacePlugin
,
261 &flipped_window_x
, &flipped_window_y
,
262 NPCoordinateSpaceFlippedWindow
);
264 CGRect main_display_bounds
= CGDisplayBounds(CGMainDisplayID());
266 // Check that all the coordinates are right. The constants below are based on
267 // the window frame set in the UI test and the content offset in the test
268 // html. Y-coordinates are not checked exactly so that the test is robust
269 // against toolbar changes, info and bookmark bar visibility, etc.
270 const int kWindowHeight
= 400;
271 const int kWindowXOrigin
= 50;
272 const int kWindowYOrigin
= 50;
273 const int kPluginXContentOffset
= 50;
274 const int kPluginYContentOffset
= 50;
275 const int kChromeYTolerance
= 200;
277 std::string error_string
;
278 if (screen_x
!= flipped_screen_x
)
279 error_string
= "Flipping screen coordinates shouldn't change x";
280 else if (flipped_screen_y
!= main_display_bounds
.size
.height
- screen_y
)
281 error_string
= "Flipped screen coordinates should be flipped vertically";
282 else if (screen_x
!= kWindowXOrigin
+ kPluginXContentOffset
)
283 error_string
= "Screen x location is wrong";
284 else if (flipped_screen_y
< kWindowYOrigin
+ kPluginYContentOffset
||
285 flipped_screen_y
> kWindowYOrigin
+ kPluginYContentOffset
+
287 error_string
= "Screen y location is wrong";
288 else if (window_x
!= flipped_window_x
)
289 error_string
= "Flipping window coordinates shouldn't change x";
290 else if (flipped_window_y
!= kWindowHeight
- window_y
)
291 error_string
= "Flipped window coordinates should be flipped vertically";
292 else if (window_x
!= kPluginXContentOffset
)
293 error_string
= "Window x location is wrong";
294 else if (flipped_window_y
< kPluginYContentOffset
||
295 flipped_window_y
> kPluginYContentOffset
+ kChromeYTolerance
)
296 error_string
= "Window y location is wrong";
298 if (!error_string
.empty()) {
299 error_string
.append(" - ");
300 error_string
.append(StringForPoint(screen_x
, screen_y
));
301 error_string
.append(" - ");
302 error_string
.append(StringForPoint(flipped_screen_x
, flipped_screen_y
));
303 error_string
.append(" - ");
304 error_string
.append(StringForPoint(window_x
, window_y
));
305 error_string
.append(" - ");
306 error_string
.append(StringForPoint(flipped_window_x
, flipped_window_y
));
307 SetError(error_string
);
310 SetError("Unimplemented");
312 SignalTestCompleted();
315 } // namespace NPAPIClient