| // 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" |
| |
| // 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; |
| #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 |