summaryrefslogtreecommitdiffstats
path: root/content/test/plugin/plugin_windowless_test.cc
blob: 9b126c9247c88ac09568e23a9e1cb660971aa5d1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#define STRSAFE_NO_DEPRECATE

#include "content/test/plugin/plugin_windowless_test.h"

#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "content/test/plugin/plugin_client.h"

#if defined(TOOLKIT_GTK)
#include <gdk/gdkx.h>
#endif

// NPEvent does not exist on the Mac.
#if defined(OS_MACOSX)
typedef NPCocoaEvent WindowlessPluginTestEvent;
#else
typedef NPEvent WindowlessPluginTestEvent;
#endif

namespace NPAPIClient {

namespace {

// Remember the first plugin instance for tests involving multiple instances.
WindowlessPluginTest* g_other_instance = NULL;

void OnFinishTest(void* data) {
  static_cast<WindowlessPluginTest*>(data)->SignalTestCompleted();
}

bool IsPaintEvent(WindowlessPluginTestEvent* np_event) {
#if defined(OS_WIN)
  return np_event->event == WM_PAINT;
#elif defined(OS_MACOSX)
  return np_event->type == NPCocoaEventDrawRect;
#elif defined(TOOLKIT_GTK)
  return np_event->type == GraphicsExpose;
#else
  NOTIMPLEMENTED();
  return false;
#endif
}

bool IsMouseUpEvent(WindowlessPluginTestEvent* np_event) {
#if defined(OS_WIN)
  return np_event->event == WM_LBUTTONUP;
#elif defined(OS_MACOSX)
  return np_event->type == NPCocoaEventMouseUp;
#else
  NOTIMPLEMENTED();
  return false;
#endif
}

#if defined(OS_MACOSX)
bool IsWindowActivationEvent(WindowlessPluginTestEvent* np_event) {
  return np_event->type == NPCocoaEventWindowFocusChanged &&
         np_event->data.focus.hasFocus;
}
#endif

}  // namespace

WindowlessPluginTest::WindowlessPluginTest(NPP id,
                                           NPNetscapeFuncs *host_functions)
    : PluginTest(id, host_functions),
      paint_counter_(0) {
  if (!g_other_instance)
    g_other_instance = this;
}

bool WindowlessPluginTest::IsWindowless() const {
  return true;
}

NPError WindowlessPluginTest::New(uint16 mode, int16 argc,
                                 const char* argn[], const char* argv[],
                                 NPSavedData* saved) {
  NPError error = PluginTest::New(mode, argc, argn, argv, saved);

  if (test_name() == "invoke_js_function_on_create") {
    ExecuteScript(
        NPAPIClient::PluginClient::HostFunctions(), g_other_instance->id(),
        "PluginCreated();", NULL);
  }

  return error;
}

int16 WindowlessPluginTest::HandleEvent(void* event) {
  NPNetscapeFuncs* browser = NPAPIClient::PluginClient::HostFunctions();

  NPBool supports_windowless = 0;
  NPError result = browser->getvalue(id(), NPNVSupportsWindowless,
                                     &supports_windowless);
  if ((result != NPERR_NO_ERROR) || (!supports_windowless)) {
    SetError("Failed to read NPNVSupportsWindowless value");
    SignalTestCompleted();
    return PluginTest::HandleEvent(event);
  }

  WindowlessPluginTestEvent* np_event =
      reinterpret_cast<WindowlessPluginTestEvent*>(event);
  if (IsPaintEvent(np_event)) {
    paint_counter_++;
#if defined(OS_WIN)
    HDC paint_dc = reinterpret_cast<HDC>(np_event->wParam);
    if (paint_dc == NULL) {
      SetError("Invalid Window DC passed to HandleEvent for WM_PAINT");
      SignalTestCompleted();
      return NPERR_GENERIC_ERROR;
    }

    HRGN clipping_region = CreateRectRgn(0, 0, 0, 0);
    if (!GetClipRgn(paint_dc, clipping_region)) {
      SetError("No clipping region set in window DC");
      DeleteObject(clipping_region);
      SignalTestCompleted();
      return NPERR_GENERIC_ERROR;
    }

    DeleteObject(clipping_region);
#endif

    if (test_name() == "execute_script_delete_in_paint") {
      ExecuteScriptDeleteInPaint(browser);
    } else if (test_name() == "multiple_instances_sync_calls") {
      MultipleInstanceSyncCalls(browser);
    } else if (test_name() == "resize_during_paint") {
      if (paint_counter_ == 1) {
        // So that we get another paint later.
        browser->invalidaterect(id(), NULL);
      } else if (paint_counter_ == 2) {
        // Do this in the second paint since that's asynchronous. The first
        // paint will always be synchronous (since the renderer process doesn't
        // have a cache of the plugin yet). If we try calling NPN_Evaluate while
        // WebKit is painting, it will assert since style recalc is happening
        // during painting.
        ExecuteScriptResizeInPaint(browser);

        // So that we can exit the test after the message loop is unrolled.
        browser->pluginthreadasynccall(id(), OnFinishTest, this);
      }
    }
#if defined(OS_MACOSX)
  } else if (IsWindowActivationEvent(np_event) &&
             test_name() == "convert_point") {
      ConvertPoint(browser);
#endif
  } else if (IsMouseUpEvent(np_event) &&
             test_name() == "execute_script_delete_in_mouse_up") {
    ExecuteScript(browser, id(), "DeletePluginWithinScript();", NULL);
    SignalTestCompleted();
  } else if (IsMouseUpEvent(np_event) &&
             test_name() == "delete_frame_test") {
    ExecuteScript(
        browser, id(),
        "parent.document.getElementById('frame').outerHTML = ''", NULL);
  }
  // If this test failed, then we'd have crashed by now.
  return PluginTest::HandleEvent(event);
}

NPError WindowlessPluginTest::ExecuteScript(NPNetscapeFuncs* browser, NPP id,
    const std::string& script, NPVariant* result) {
  std::string script_url = "javascript:";
  script_url += script;

  size_t script_length = script_url.length();
  if (script_length != static_cast<uint32_t>(script_length)) {
    return NPERR_GENERIC_ERROR;
  }

  NPString script_string = { script_url.c_str(),
                             static_cast<uint32_t>(script_length) };
  NPObject *window_obj = NULL;
  browser->getvalue(id, NPNVWindowNPObject, &window_obj);

  NPVariant unused_result;
  if (!result)
    result = &unused_result;

  return browser->evaluate(id, window_obj, &script_string, result);
}

void WindowlessPluginTest::ExecuteScriptDeleteInPaint(
    NPNetscapeFuncs* browser) {
  const NPUTF8* urlString = "javascript:DeletePluginWithinScript()";
  const NPUTF8* targetString = NULL;
  browser->geturl(id(), urlString, targetString);
  SignalTestCompleted();
}

void WindowlessPluginTest::ExecuteScriptResizeInPaint(
    NPNetscapeFuncs* browser) {
  ExecuteScript(browser, id(), "ResizePluginWithinScript();", NULL);
}

void WindowlessPluginTest::MultipleInstanceSyncCalls(NPNetscapeFuncs* browser) {
  if (this == g_other_instance)
    return;

  DCHECK(g_other_instance);
  ExecuteScript(browser, g_other_instance->id(), "TestCallback();", NULL);
  SignalTestCompleted();
}

#if defined(OS_MACOSX)
std::string StringForPoint(int x, int y) {
  std::string point_string("(");
  point_string.append(base::IntToString(x));
  point_string.append(", ");
  point_string.append(base::IntToString(y));
  point_string.append(")");
  return point_string;
}
#endif

void WindowlessPluginTest::ConvertPoint(NPNetscapeFuncs* browser) {
#if defined(OS_MACOSX)
  // First, just sanity-test that round trips work.
  NPCoordinateSpace spaces[] = { NPCoordinateSpacePlugin,
                                 NPCoordinateSpaceWindow,
                                 NPCoordinateSpaceFlippedWindow,
                                 NPCoordinateSpaceScreen,
                                 NPCoordinateSpaceFlippedScreen };
  for (unsigned int i = 0; i < arraysize(spaces); ++i) {
    for (unsigned int j = 0; j < arraysize(spaces); ++j) {
      double x, y, round_trip_x, round_trip_y;
      if (!(browser->convertpoint(id(), 0, 0, spaces[i], &x, &y, spaces[j])) ||
          !(browser->convertpoint(id(), x, y, spaces[j], &round_trip_x,
                                  &round_trip_y, spaces[i]))) {
        SetError("Conversion failed");
        SignalTestCompleted();
        return;
      }
      if (i != j && x == 0 && y == 0) {
        SetError("Converting a coordinate should change it");
        SignalTestCompleted();
        return;
      }
      if (round_trip_x != 0 || round_trip_y != 0) {
        SetError("Round-trip conversion should return the original point");
        SignalTestCompleted();
        return;
      }
    }
  }

  // Now, more extensive testing on a single point.
  double screen_x, screen_y;
  browser->convertpoint(id(), 0, 0, NPCoordinateSpacePlugin,
                        &screen_x, &screen_y, NPCoordinateSpaceScreen);
  double flipped_screen_x, flipped_screen_y;
  browser->convertpoint(id(), 0, 0, NPCoordinateSpacePlugin,
                        &flipped_screen_x, &flipped_screen_y,
                        NPCoordinateSpaceFlippedScreen);
  double window_x, window_y;
  browser->convertpoint(id(), 0, 0, NPCoordinateSpacePlugin,
                        &window_x, &window_y, NPCoordinateSpaceWindow);
  double flipped_window_x, flipped_window_y;
  browser->convertpoint(id(), 0, 0, NPCoordinateSpacePlugin,
                        &flipped_window_x, &flipped_window_y,
                        NPCoordinateSpaceFlippedWindow);

  CGRect main_display_bounds = CGDisplayBounds(CGMainDisplayID());

  // Check that all the coordinates are right. The constants below are based on
  // the window frame set in the UI test and the content offset in the test
  // html. Y-coordinates are not checked exactly so that the test is robust
  // against toolbar changes, info and bookmark bar visibility, etc.
  const int kWindowHeight = 400;
  const int kWindowXOrigin = 50;
  const int kWindowYOrigin = 50;
  const int kPluginXContentOffset = 50;
  const int kPluginYContentOffset = 50;
  const int kChromeYTolerance = 200;

  std::string error_string;
  if (screen_x != flipped_screen_x)
    error_string = "Flipping screen coordinates shouldn't change x";
  else if (flipped_screen_y != main_display_bounds.size.height - screen_y)
    error_string = "Flipped screen coordinates should be flipped vertically";
  else if (screen_x != kWindowXOrigin + kPluginXContentOffset)
    error_string = "Screen x location is wrong";
  else if (flipped_screen_y < kWindowYOrigin + kPluginYContentOffset ||
           flipped_screen_y > kWindowYOrigin + kPluginYContentOffset +
                              kChromeYTolerance)
    error_string = "Screen y location is wrong";
  else if (window_x != flipped_window_x)
    error_string = "Flipping window coordinates shouldn't change x";
  else if (flipped_window_y != kWindowHeight - window_y)
    error_string = "Flipped window coordinates should be flipped vertically";
  else if (window_x != kPluginXContentOffset)
    error_string = "Window x location is wrong";
  else if (flipped_window_y < kPluginYContentOffset ||
           flipped_window_y > kPluginYContentOffset + kChromeYTolerance)
    error_string = "Window y location is wrong";

  if (!error_string.empty()) {
    error_string.append(" - ");
    error_string.append(StringForPoint(screen_x, screen_y));
    error_string.append(" - ");
    error_string.append(StringForPoint(flipped_screen_x, flipped_screen_y));
    error_string.append(" - ");
    error_string.append(StringForPoint(window_x, window_y));
    error_string.append(" - ");
    error_string.append(StringForPoint(flipped_window_x, flipped_window_y));
    SetError(error_string);
  }
#else
  SetError("Unimplemented");
#endif
  SignalTestCompleted();
}

}  // namespace NPAPIClient