• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #define STRSAFE_NO_DEPRECATE
6 
7 #include "content/test/plugin/plugin_windowless_test.h"
8 
9 #include "base/strings/string_number_conversions.h"
10 #include "base/strings/string_util.h"
11 #include "content/test/plugin/plugin_client.h"
12 
13 // NPEvent does not exist on the Mac.
14 #if defined(OS_MACOSX)
15 typedef NPCocoaEvent WindowlessPluginTestEvent;
16 #else
17 typedef NPEvent WindowlessPluginTestEvent;
18 #endif
19 
20 namespace NPAPIClient {
21 
22 namespace {
23 
24 // Remember the first plugin instance for tests involving multiple instances.
25 WindowlessPluginTest* g_other_instance = NULL;
26 
OnFinishTest(void * data)27 void OnFinishTest(void* data) {
28   static_cast<WindowlessPluginTest*>(data)->SignalTestCompleted();
29 }
30 
IsPaintEvent(WindowlessPluginTestEvent * np_event)31 bool IsPaintEvent(WindowlessPluginTestEvent* np_event) {
32 #if defined(OS_WIN)
33   return np_event->event == WM_PAINT;
34 #elif defined(OS_MACOSX)
35   return np_event->type == NPCocoaEventDrawRect;
36 #else
37   NOTIMPLEMENTED();
38   return false;
39 #endif
40 }
41 
IsMouseUpEvent(WindowlessPluginTestEvent * np_event)42 bool IsMouseUpEvent(WindowlessPluginTestEvent* np_event) {
43 #if defined(OS_WIN)
44   return np_event->event == WM_LBUTTONUP;
45 #elif defined(OS_MACOSX)
46   return np_event->type == NPCocoaEventMouseUp;
47 #else
48   NOTIMPLEMENTED();
49   return false;
50 #endif
51 }
52 
53 #if defined(OS_MACOSX)
IsWindowActivationEvent(WindowlessPluginTestEvent * np_event)54 bool IsWindowActivationEvent(WindowlessPluginTestEvent* np_event) {
55   return np_event->type == NPCocoaEventWindowFocusChanged &&
56          np_event->data.focus.hasFocus;
57 }
58 #endif
59 
60 }  // namespace
61 
WindowlessPluginTest(NPP id,NPNetscapeFuncs * host_functions)62 WindowlessPluginTest::WindowlessPluginTest(NPP id,
63                                            NPNetscapeFuncs *host_functions)
64     : PluginTest(id, host_functions),
65       paint_counter_(0) {
66   if (!g_other_instance)
67     g_other_instance = this;
68 }
69 
IsWindowless() const70 bool WindowlessPluginTest::IsWindowless() const {
71   return true;
72 }
73 
New(uint16 mode,int16 argc,const char * argn[],const char * argv[],NPSavedData * saved)74 NPError WindowlessPluginTest::New(uint16 mode, int16 argc,
75                                  const char* argn[], const char* argv[],
76                                  NPSavedData* saved) {
77   NPError error = PluginTest::New(mode, argc, argn, argv, saved);
78 
79   if (test_name() == "invoke_js_function_on_create") {
80     ExecuteScript(
81         NPAPIClient::PluginClient::HostFunctions(), g_other_instance->id(),
82         "PluginCreated();", NULL);
83   }
84 
85   return error;
86 }
87 
HandleEvent(void * event)88 int16 WindowlessPluginTest::HandleEvent(void* event) {
89   NPNetscapeFuncs* browser = NPAPIClient::PluginClient::HostFunctions();
90 
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);
98   }
99 
100   WindowlessPluginTestEvent* np_event =
101       reinterpret_cast<WindowlessPluginTestEvent*>(event);
102   if (IsPaintEvent(np_event)) {
103     paint_counter_++;
104 #if defined(OS_WIN)
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;
110     }
111 
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;
118     }
119 
120     DeleteObject(clipping_region);
121 #endif
122 
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
136         // during painting.
137         ExecuteScriptResizeInPaint(browser);
138 
139         // So that we can exit the test after the message loop is unrolled.
140         browser->pluginthreadasynccall(id(), OnFinishTest, this);
141       }
142     }
143 #if defined(OS_MACOSX)
144   } else if (IsWindowActivationEvent(np_event) &&
145              test_name() == "convert_point") {
146       ConvertPoint(browser);
147 #endif
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") {
154     ExecuteScript(
155         browser, id(),
156         "parent.document.getElementById('frame').outerHTML = ''", NULL);
157   }
158   // If this test failed, then we'd have crashed by now.
159   return PluginTest::HandleEvent(event);
160 }
161 
ExecuteScript(NPNetscapeFuncs * browser,NPP id,const std::string & script,NPVariant * result)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;
166 
167   size_t script_length = script_url.length();
168   if (script_length != static_cast<uint32_t>(script_length)) {
169     return NPERR_GENERIC_ERROR;
170   }
171 
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);
176 
177   NPVariant unused_result;
178   if (!result)
179     result = &unused_result;
180 
181   return browser->evaluate(id, window_obj, &script_string, result);
182 }
183 
ExecuteScriptDeleteInPaint(NPNetscapeFuncs * browser)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();
190 }
191 
ExecuteScriptResizeInPaint(NPNetscapeFuncs * browser)192 void WindowlessPluginTest::ExecuteScriptResizeInPaint(
193     NPNetscapeFuncs* browser) {
194   ExecuteScript(browser, id(), "ResizePluginWithinScript();", NULL);
195 }
196 
MultipleInstanceSyncCalls(NPNetscapeFuncs * browser)197 void WindowlessPluginTest::MultipleInstanceSyncCalls(NPNetscapeFuncs* browser) {
198   if (this == g_other_instance)
199     return;
200 
201   DCHECK(g_other_instance);
202   ExecuteScript(browser, g_other_instance->id(), "TestCallback();", NULL);
203   SignalTestCompleted();
204 }
205 
206 #if defined(OS_MACOSX)
StringForPoint(int x,int y)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(")");
213   return point_string;
214 }
215 #endif
216 
ConvertPoint(NPNetscapeFuncs * browser)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();
233         return;
234       }
235       if (i != j && x == 0 && y == 0) {
236         SetError("Converting a coordinate should change it");
237         SignalTestCompleted();
238         return;
239       }
240       if (round_trip_x != 0 || round_trip_y != 0) {
241         SetError("Round-trip conversion should return the original point");
242         SignalTestCompleted();
243         return;
244       }
245     }
246   }
247 
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);
263 
264   CGRect main_display_bounds = CGDisplayBounds(CGMainDisplayID());
265 
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;
276 
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 +
286                               kChromeYTolerance)
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";
297 
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);
308   }
309 #else
310   SetError("Unimplemented");
311 #endif
312   SignalTestCompleted();
313 }
314 
315 }  // namespace NPAPIClient
316